We're using drawer navigator in our app to show the routes in the drawer. Our routes have stacks of pages and users can go into a page. E.g.:
Home
HomeChild1
HomeChild2
About
When user goes to the HomeChild1 or HomeChild2 we want to show back button in the header. We don't want to add a back button in each page's navigation options like below:
HomeScreen1.navigationOptions = ({ navigation }) => {
const { state } = navigation;
return {
title: `Home Screen 1`,
headerLeft: (
<Icon
name="ios-arrow-back"
type="ionicon"
color="#FFF"
underlayColor="transparent"
iconStyle={{ paddingRight: 5 }}
onPress={() => {
navigation.navigate.goBack();
}}
/>
)
};
}
Is there a way to put this config at the global level - like in the defaultNavigationOptions.
const defaultNavigationOptions = ({ navigation }) => {
return {
hideStatusBar: false,
headerStyle: {
backgroundColor: Colors.baseColor
},
headerTintColor: Colors.titleColor,
headerBackImage: ( //this has no affect
<Icon
name="ios-arrow-back"
type="Ionicons"
style={{ color: "#D8025E", fontSize: 30, paddingHorizontal: 10 }}
/>
),
headerLeft: (
<Icon
name="menu"
size={30}
style={{ marginStart: 25 }}
color="#FFF"
backgroundColor="#FFF"
onPress={() => navigation.openDrawer()}
/>
)
};
};
You can set up using navigation path values.
defaultNavigationOptions: ({ navigation }) => ({
headerLeft: () => {
const { routeName } = navigation.state;
let iconName;
if (routeName === "HomeChild1") {
iconName = "ios-arrow-back";
} else if (routeName === "HomeChild2") {
iconName = "ios-arrow-back";
}
....
return (
<Icon
name={iconName}
size={30}
style={{ marginStart: 25 }}
color="#FFF"
backgroundColor="#FFF"
onPress={() => navigation.goBack()}
/>
);
...
Related
I have a simple react native app with two screens.
First screen is the list, where you see your selected groups, and you can remove them, by clicking on trash icon:
export const Settings: NavioScreen = observer(({ }) => {
...
return (
<View flex bg-bgColor padding-20>
<FlashList
contentInsetAdjustmentBehavior="always"
data={toJS(ui.savedGroups)}
renderItem={({item}) => <ListItem item={item} />}
estimatedItemSize={20}
/>
</View>
);
});
};
const ListItem = ({item}: any) => {
const { ui } = useStores();
return (
<View>
<Text textColor style={{ fontWeight: 'bold', fontSize: 15 }}>{item.name}</Text>
<TouchableOpacity onPress={() => ui.deleteGroup(item)}>
<Icon name={'trash'}/>
</TouchableOpacity>
</View>
);
};
The second screen is also the list, where you can add and remove the subjects from the list:
export const Playground: NavioScreen = observer(() => {
...
const groupsToShow =
ui.search && ui.search.length > 0
? ui.groups.filter((p) =>
p.name.toLowerCase().includes(ui.search.toLowerCase())
)
: ui.groups;
return (
<View >
<FlashList
data={toJS(groupsToShow)}
renderItem={({item}) => <ListItem item={item} />}
/>
</View>
);
});
const ListItem = ({item}: any) => {
const { ui } = useStores();
return (
<View>
<Text textColor style={{ fontWeight: 'bold', fontSize: 15 }}>{item.name}</Text>
<View>
<If
_={ui.isGroupSaved(item)}
_then={
<TouchableOpacity onPress={(e) => {ui.deleteGroup(item)}}>
<AntDesign name="heart" size={20} color={Colors.primary} />
</TouchableOpacity>
}
_else={
<TouchableOpacity onPress={(e) => {ui.addGroup(item)}}>
<AntDesign name="hearto" size={20} color={Colors.primary} />
</TouchableOpacity>
}
/>
</View>
</View>
);
};
And now when I remove the group from the first list, the heart icon do not update on the second list. But it should, because there is an if statement on second list, that checks if the group is saved. And if it is not, the heart should have the name="hearto"
I have tried to use the state instead mobx library but it does not also help.
Here is my store written with mobx:
export class UIStore implements IStore {
savedGroups = [];
constructor() {
makeAutoObservable(this);
makePersistable(this, {
name: UIStore.name,
properties: ['savedGroups'],
});
}
addGroup = (group: any) => {
if (true === this.isGroupSaved(group)) {
return;
}
this.savedGroups.push(group);
}
isGroupSaved = (group: any) => {
return -1 !== this.savedGroups.findIndex(g => g.id === group.id);
}
deleteGroup = (groupToDelete: any) => {
this.savedGroups = this.savedGroups.filter((group) => group.id !== groupToDelete.id);
}
}
I want to place Facebook ads to React native using react-native-fbads. I am using Bottom tab navigator in my application and I want the ad to be fixed floating over the bottom tab.
I can place the ad on every tab screen but I don't want to place the ad on every screen and instead, I want to place it just over the bottom tab so that it is visible on every tab user visits.
Something like this:
Code: TabNavigator.js
<Tab.Navigator
screenOptions={({ route }) => ({
headerShown: false,
})}
tabBarOptions={{
activeTintColor: '#001B79',
inactiveTintColor: 'gray',
}}
>
<Tab.Screen name="Home1" component={Home11} />
<Tab.Screen name="Home2" component={Home12} />
</Tab.Navigator>
I want to add <BannerAd /> so that it will work like I want it to be.
just try to use bottom-tab-navigator! regularly!
// ...
import { View, Text, TouchableOpacity } from 'react-native';
function MyTabBar({ state, descriptors, navigation }) {
return (
<View style={{ flexDirection: 'row' }}>
{state.routes.map((route, index) => {
const { options } = descriptors[route.key];
const label =
options.tabBarLabel !== undefined
? options.tabBarLabel
: options.title !== undefined
? options.title
: route.name;
const isFocused = state.index === index;
const onPress = () => {
const event = navigation.emit({
type: 'tabPress',
target: route.key,
canPreventDefault: true,
});
if (!isFocused && !event.defaultPrevented) {
// The `merge: true` option makes sure that the params inside the tab screen are preserved
navigation.navigate({ name: route.name, merge: true });
}
};
const onLongPress = () => {
navigation.emit({
type: 'tabLongPress',
target: route.key,
});
};
return (
<TouchableOpacity
accessibilityRole="button"
accessibilityState={isFocused ? { selected: true } : {}}
accessibilityLabel={options.tabBarAccessibilityLabel}
testID={options.tabBarTestID}
onPress={onPress}
onLongPress={onLongPress}
style={{ flex: 1 }}
>
<Text style={{ color: isFocused ? '#673ab7' : '#222' }}>
{label}
</Text>
</TouchableOpacity>
);
})}
</View>
);
}
<Tab.Navigator tabBar={props => <MyTabBar {...props} />}>
{...}
</Tab.Navigator>
After not succeeding in solving my problem here, I had to create a new question with all my classes in it.
I am new to react native and have a problem figuring out how to navigate from one class to another one with passing parameters and would appreciate your help.
All I want to do is:
SessionCreate with flatlist containing CustomButton
Navigate from SessionCreate to ItemConfig by clicking CustomButton
Pass parameter "element" to ItemConfig
Show content of parameter passed in ItemConfig
With this setup an empty "element" is passed as parameter to the ItemConfigScreen (but no error occurs):
app.js:
//Views
function Home({ navigation }) {
return (
<HomeScreen />
);
}
function Session({ navigation }) {
return (
<SessionScreen />
);
}
//subviews
function SessionCreate({ navigation }) {
return (
<SessionCreateScreen />
);
}
function ItemConfig({ navigation }) {
return (
<ItemConfigScreen />
);
}
//navigation stacks
const SessionStack = createStackNavigator();
function SessionStackScreen({ navigation }) {
return (
<SessionStack.Navigator>
<SessionStack.Screen
name="Session"
component={Session}
options={{ tabBarLabel: 'Session!' }}
/>
<SessionStack.Screen
name="SessionCreate"
component={SessionCreate}
options={{ tabBarLabel: 'SessionCreate!' }}
/>
<SessionStack.Screen
name="ItemConfig"
component={ItemConfig}
options={{ tabBarLabel: 'ItemConfig!' }}
/>
</SessionStack.Navigator>
);
}
//Navbar Bottom
const Tab = createBottomTabNavigator();
function App() {
return (
<View style={[theme.colcontainer, { flexDirection: "column" }]} >
<NavigationContainer>
<Tab.Navigator
screenOptions={({ route }) => ({
tabBarIcon: ({ focused, color, size }) => {
let iconName;
if (route.name === 'Home') {
iconName = focused ? 'home' : 'home-outline';
} else if (route.name === 'Session') {
iconName = focused ? 'book' : 'book-outline';
}
// dynamic ionicon
return <Ionicons name={iconName} size={size} color={color} />;
},
})}
>
<Tab.Screen name="Home" component={Home} />
<Tab.Screen name="Session" component={SessionStackScreen} />
</Tab.Navigator>
</NavigationContainer>
</View >
);
}
export default App;
SessionScreen.js:
function SessionScreen() {
const navigation = useNavigation();
return (
<View style={[theme.container, { flexDirection: "column" }]} >
<View>
<TouchableOpacity onPress={() => navigation.navigate('SessionCreate')}>
<Text >Create Session</Text>
</TouchableOpacity>
</View>
</View >
);
}
export default SessionScreen;
SessionCreateScreen.js:
//data
const sessionElements = [
{
id: "1",
title: "title1"
}
];
function SessionCreateScreen() {
const navigation = useNavigation()
const renderItemConfiguration = ({ item }) => (
<CustomButton element={item.title} onPress={() => navigation.navigate('ItemConfig', { element: 'item.title' })} />
);
return (
<View style={{ flexDirection: "column", flex: 2}} >
<SafeAreaView >
<FlatList
data={sessionElements}
renderItem={renderItemConfiguration}
keyExtractor={(item) => item.id}
/>
</SafeAreaView>
</View >
);
}
export default SessionCreateScreen;
ItemConfigScreen.js:
const element = "";
function ItemConfigScreen() {
return (
<ScrollView >
<View style={{ flexDirection: "column", flex: 2}} >
<Text>Configure {element} here</Text>
</View >
</ScrollView>
);
}
export default ItemConfigScreen;
Any help is appreciated.
To get parameters in ItemConfigScreen you have to use the useRoute hook from the react-navigation package.
you can read more about it here useRoute
import {useRoute} from '#react-navigation/native';
function ItemConfigScreen() {
const route = useRoute();
const element = route.params?.element;
return (
<ScrollView >
<View style={{ flexDirection: "column", flex: 2}} >
<Text>Configure {element} here</Text>
</View >
</ScrollView>
);
}
There is also a mistake in your onPress navigation call in CustomButton, instead of 'item.title' you will have to pass ${item.title} then only actual data will be passed. JS Template Literals
<CustomButton element={item.title} onPress={() => navigation.navigate('ItemConfig', { element: `${item.title}` })} />
I am using React Native App, I have a main page that displays the categories, and a button that navigates to another page to add a new category,
the issue is that when I add a new one I want to update the main page as well to include the new one,
is there a way to do so, this my code,
this the navigation
const HomeStack = createStackNavigator();
const Tab = createMaterialBottomTabNavigator();
const MainTabScreen = () => (
<Tab.Navigator
initialRouteName="Home"
activeColor="#fff"
>
<Tab.Screen
name="Home"
component={HomeStackScreen}
options={{
tabBarLabel: 'Home',
tabBarColor: '#009387',
tabBarIcon: ({ color }) => (
<Icon name="ios-home" color={color} size={26} />
),
}}
/>
);
export default MainTabScreen;
const HomeStackScreen = ({navigation}) => (
<HomeStack.Navigator screenOptions={{
headerStyle: {
backgroundColor: '#009387',
},
headerTintColor: '#fff',
headerTitleStyle: {
fontWeight: 'bold'
}
}}>
<HomeStack.Screen name="Home" component={HomeScreen} options={{
title:'Overview',
headerLeft: () => (
<Icon.Button name="ios-menu" size={25} backgroundColor="#009387" onPress={() => navigation.openDrawer()}></Icon.Button>
)
}} />
<HomeStack.Screen name="Home_Provider" component={HomeScreen_Provider} options={{
title:'Overview',
headerLeft: () => (
<Icon.Button name="ios-menu" size={25} backgroundColor="#009387" onPress={() => navigation.openDrawer()}></Icon.Button>
)
}} />
<HomeStack.Screen name="AddServiceScreen" component={AddServiceScreen} options={{
title:'Overview',
headerLeft: () => (
<Icon.Button name="ios-menu" size={25} backgroundColor="#009387" onPress={() => navigation.openDrawer()}></Icon.Button>
)
}} />
</HomeStack.Navigator>
);
this is the home page:
const HomeScreen_Provider = ({navigation}) => {
const [services, setServices] = React.useState([]);
const [status, setStatus] = React.useState([]);
async function fetchdata(){
api.GetMyServices({headers:{'Accept': "application/json", 'content-type': "application/json"}})
.then(function (response) {
console.log(response.data);
setServices(response.data);
})
.catch(function (error) {
console.error(error);
});
}
React.useEffect(() => {
fetchdata();
}, []);
return (
<View style={styles.container}>
<StatusBar backgroundColor='#009387' barStyle="light-content"/>
<View style={{marginTop:20}}>
<Text style={styles.text_header}>My Services</Text>
</View>
<TouchableOpacity
style={{alignItems:'flex-end',marginLeft:250}}
onPress={() => navigation.navigate('AddServiceScreen')}
>
<Text style={styles.button}>Add New..</Text>
</TouchableOpacity>
{services.length == 0 ? <Text >You Have No Services Yet, Click Here To Add..</Text> : <View/>}
<ScrollView>
{services.map (service => (
<View style ={styles.rowContainer}>
<Text style={styles.text_footer}>{service.name} </Text>
<Text style={styles.text_footer}>{service.status} </Text>
<TouchableOpacity
onPress={handleToggle}
>
<Text style={styles.button}>Change Status</Text>
</TouchableOpacity>
</View>
))}
</ScrollView>
</View>
)
};
export default HomeScreen_Provider;
````````````
here is the add new page :
const AddServiceScreen = ({navigation}) => {
const { colors } = useTheme();
const [availableServices, setAvailableServices] = React.useState([]);
const [userID, setUserID] = React.useState(0);
const [serviceId, setServiceId] = React.useState(0);
const [serviceName, setServiceName] = React.useState(0);
const [serviceStatus, setServiceStatus] = React.useState('');
const [serviceCost, setServiceCost] = React.useState(0);
async function handleAddServiceButton () {
api.AddNewCategory(Category,{headers:{'Accept': "application/json", 'content-type': "application/json"}})
.then(function (response) {
})
.catch(function (error) {
console.error(error);
});
}
return (
<View style={styles.container}>
<StatusBar barStyle= { theme.dark ? "light-content" : "dark-content" }/>
<View style={styles.rowContainer} >
{availableServices.length !== 0 ? (
<View>
<Text style={{marginTop:15,marginBottom:30}}>Select From Existing Services</Text>
<Picker
selectedValue={serviceId}
style={{ height: 50, width: 150 }}
onValueChange={(itemValue, itemIndex) => setServiceId(itemValue)}
>
{availableServices.map((service)=>{
return(
<Picker.Item label={service.name} value={service.id} />
)
})}
</Picker>
</View>
): <View/>}
</View>
<Button
title="Submit"
onPress={handleAddServiceButton}
/>
</View>
);
};
export default AddServiceScreen;
You can either use react context to update the state directly and use the spread operator in the API call
or
You can maintain your state using redux to centralize your application state
On my navigation tab, I want to change the color of the label + the color of the icon when its active, so what I did is created an if and else statement:
<MealsFavTabNavigator.Screen
name="Favorites"
component={FavoritesScreen}
options={({ route }) => ({
tabBarIcon: (tabInfo) => {
if (route.name === 'Favorites'){
return <Ionicons name="ios-star" size={25} color='tomato'/>
} else {
return <Ionicons name="ios-star" size={25} color='black' />
}
}
})}
My label color is also fixed above the Navigator level:
<MealsFavTabNavigator.Navigator
tabBarOptions={{
activeTintColor: Colors.primaryColor,
inactiveTintColor: 'black',
}}>
Here's my full code:
import React from 'react';
import { NavigationContainer } from '#react-navigation/native';
import { createStackNavigator } from '#react-navigation/stack';
import { Ionicons } from '#expo/vector-icons';
import CategoriesScreen from '../screens/CategoriesScreen';
import CategoryMealsScreen from '../screens/CategoryMealsScreen';
import MealDetailScreen from '../screens/MealDetailScreen';
import FavoritesScreen from '../screens/FavoritesScreen';
import HeaderButton from '../components/HeaderButton';
import { HeaderButtons, Item } from 'react-navigation-header-buttons';
import { createBottomTabNavigator } from '#react-navigation/bottom-tabs';
import { CATEGORIES } from '../data/dummy-data';
import Colors from '../constants/colors';
const MealsNav = createStackNavigator();
const MealsNavigator = () => {
return (
<MealsNav.Navigator
mode="modal"
screenOptions={{
headerStyle: {
backgroundColor: Colors.primaryColor,
},
headerTintColor: '#fff',
headerTitleStyle: {
fontSize: 17
}
}}
>
<MealsNav.Screen
name="Categories"
component={CategoriesScreen}
options={{
title: 'Meals Categories'
}}
/>
<MealsNav.Screen
name="CategoryMeals"
component={CategoryMealsScreen}
options={({ route }) => {
const catId = route.params.categoryId;
const selectedCategory = CATEGORIES.find((cat) => cat.id === catId);
return {
title: selectedCategory.title,
};
}}
/>
<MealsNav.Screen
name="MealDetail"
component={MealDetailScreen}
options={{
title: 'Meal Detail',
headerRight: () => (
<HeaderButtons HeaderButtonComponent={HeaderButton}>
<Item
title='Favorite'
iconName='ios-star'
onPress={() => console.log('Mark as the favorite')}
/>
</HeaderButtons>
),
}}
/>
</MealsNav.Navigator>
);
};
const MealsFavTabNavigator = createBottomTabNavigator();
const MealsTabNav = () => {
return (
<NavigationContainer>
<MealsFavTabNavigator.Navigator
tabBarOptions={{
activeTintColor: Colors.primaryColor,
inactiveTintColor: 'black',
}}>
<MealsFavTabNavigator.Screen
name="Meals"
component={MealsNavigator}
options={({ route }) => ({
tabBarIcon: ({ color }) => {
if(route.name === 'Meals'){
color = 'tomato'
} else if (route.name === 'Favorites') {
color = 'black'
}
return <Ionicons name="ios-restaurant" size={25} color={color}/>
}
})}
/>
<MealsFavTabNavigator.Screen
name="Favorites"
component={FavoritesScreen}
options={({ route }) => ({
tabBarIcon: (tabInfo) => {
if (route.name === 'Favorites'){
return <Ionicons name="ios-star" size={25} color='tomato'/>
} else {
return <Ionicons name="ios-star" size={25} color='black' />
}
}
})}
/>
</MealsFavTabNavigator.Navigator>
</NavigationContainer>
);
};
export default MealsTabNav;
How can I change the color of the label and color of the iconicons when its active? My solution doesnt work.
By default there are parameters for colors in tabbar icon and tabbar label and these are set to the active tint color and inactive tint color.
But if you have a requirement to override this you can do the following
<Tab.Screen
name="Feed"
component={Feed}
options={{
tabBarLabel:({ focused,color })=>(<Text style={{color:focused?"red":"aqua"}}>1232</Text>),
tabBarIcon: ({ focused,color, size }) => (
<MaterialCommunityIcons name="home" color={focused?"green":"blue"} size={size} />
),
}}
/>
References on the props
tabBarLabel can be a text or a react node, and you get the focused and the color as arguments, The color will be the color you set as activetintcolor or inactivetintcolor.
focused is a boolean on whether the tab is focused or not.
Same arguments are passed to the tabBarIcon only difference is the size which is the size of the icon.
If you see the code above, I have given custom colors based on focused without using the color that is passed. You can do this as per your requirement.
https://stackoverflow.com/a/63033684/17735463 - This is a valid answer but mine works fine too (react-navigation-v5)
<Tab.Navigator
screenOptions={({ route }) => ({
tabBarIcon: ({ focused }) => {
let iconName;
let size=25;
let color = focused ? 'black' : 'gray';
if (route.name === 'HomeScreen') {
iconName = 'ios-home';
return <Ionicons name={iconName} size={size} color={color} />;
} else if (route.name === 'FavoritesScreen') {
iconName = 'star';
return <AntDesign name={iconName} size={size} color={color} />;
}
tabBarLabel: ({ focused }) => {
let titleStyle = {
fontSize: 12,
fontWeight: focused ? 'bold' : '500',
color: focused ? 'black' : 'gray',
};
if (route.name === 'HomeScreen') {
return <Text style={titleStyle}>Home</Text>;
} else if (route.name === 'FavoritesScreen') {
return <Text style={titleStyle}>Favorites</Text>;
}
},
})}>
<Tab.Screen name='Home' component={HomeScreen}/>
<Tab.Screen name='Favorites' component={FavoritesScreen}/>
</Tab.Navigator>