React Native - Header Title on Material Bottom Tabs on Navigation 5 - react-native

I have a Material Bottom Tabs into a Stack Navigator in Navigation 5.
It is suppose dto get the title and route it throught a function but doesn't work.
function:
function getHeaderTitle(route) {
const routeName = route.state
? route.state.routes[route.state.index].name
: route.params?.screen || 'Home';
switch (routeName) {
case 'Favorites':
return 'Favorites';
case 'Info':
return 'Info';
}
}
materialBottomTabs
const NavTab = () => {
return (
<Tab.Navigator>
<Tab.Screen
name="Favorites"
component={FavoritesScreen}
options={({route}) => ({
tabBarLabel: 'Fav',
tabBarIcon: () => IconFav(),
})}
/>
<Tab.Screen
name="Info"
component={InfoScreen}
options={({route}) => ({
headerTitle: 'Info',
tabBarLabel: 'Info',
tabBarIcon: () => IconInfo(),
})}
/>
</Tab.Navigator>
stackNavigator
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen
name="Home"
component={NavTab}
options={({route}) => ({
headerTitle: getHeaderTitle(route),
})}
/>
<Stack.Screen
name="Detail"
component={ItemDetailScreen}
/>
</Stack.Navigator>
</NavigationContainer>;
I easly set the title of a screen listed in the Stack.
Example:
const ItemDetailScreen = ({route, navigation}) => {
useEffect(() => {
navigation.setOptions({
headerTitle: item.title,
});
}, []);
But I can't set the title of the screens into the material bottom tabs. They always have the name set into the stack navigator. How can I do it?

options={({route}) => ({
headerTitle: getHeaderTitle(route),
})}
i think it should be:
options={({route}) => ({
title: getHeaderTitle(route),
})}

Related

opening drawer by pressing on bottom tab not working

I am trying to toggleDrawer on TabPress in BottomTab, but when I press on tab I get the following error navigation.toggleDrwer() is not a function.
This is my code:
export default function BottomTab() {
return (
<NavigationContainer>
<Tab.Navigator>
<Tab.Screen name="Home" component={DrawerTab} style={styles} listeners={({ navigation }) => ({
tabPress: e => {
e.preventDefault()
navigation.toggleDrawer()
},
})} />
</Tab.Navigator>
</NavigationContainer>
);
}
i have manage to find the answer. So you actually have to use dispatch.
<Tab.Screen name="Home" component={DrawerTab} style={styles} listeners={({ navigation }) => ({
tabPress: e => {
e.preventDefault()
navigation.dispatch(DrawerActions.toggleDrawer())
},
})} />

Opening the Drawer navigator from the bottom tab

I'm upgrading my App from react-navigation 4 to 5.
In version 4 I have a tab that open the drawer using the following code
const MainNavigator = createBottomTabNavigator(
{
More: {
screen: AdminNavigator,
navigationOptions: {
tabBarIcon: (tabInfo) => {
//return <Ionicons name="ios-star" size={25} color={tabInfo.tintColor} />;
return (
<Icon
name="tune"
color={tabInfo.tintColor}
size={tabInfo.focused ? 32 : 28}
style={{
paddingTop: 10,
}}
/>
);
},
tabBarColor: Colors.primary,
tabBarOnPress: ({ navigation }) => {
navigation.openDrawer();
},
},
},
}
In new version 5 I have the following Navigation config
<NavigationContainer>
<Drawer.Navigator>
<Drawer.Screen name="Home" component={TabsScreen} />
<Drawer.Screen name="Favorites" component={FavoritesStackScreen} />
<Drawer.Screen name="Language" component={LanguageStackScreen} />
</Drawer.Navigator>
</NavigationContainer>
where TabScreen is my bottom tab navigator
const TabsScreen = () => (
<Tabs.Navigator>
<Tabs.Screen name={"Home"} component={HomeStackScreen} />
<Tabs.Screen name={"Cocktails"} component={CocktailsStackScreen} />
<Tabs.Screen
name={"More"}
component={HomeStackScreen}
options={{
...
}}
/>
</Tabs.Navigator>
);
I'm looking for the equivalent in V5 of the following
tabBarOnPress: ({ navigation }) => {
navigation.openDrawer();
},
Try the following:
<Tabs.Screen
name={"More"}
component={HomeStackScreen}
listeners={({ navigation }) => ({
tabPress: e => {
e.preventDefault();
navigation.openDrawer();
}
})}
/>
https://reactnavigation.org/docs/navigation-events/#listeners-prop-on-screen

Dynamic items in Drawer nested in Stack Navigator react-navigation

i`m realy stumped about this.
I have a bottom tabs width 4 screens
BottomTabs.js
export const AllTabs = ({ navigation }) => {
return (
<Tab.Navigator initialRouteName="Home" tabBar={props => <BottomNavigation {...props} />}>
<Tab.Screen name="Home" component={HomeStack} options={{
icon: 'shopping-store'
}}/>
<Tab.Screen name="Catalog" component={CatalogStack} options={{
icon: 'pulse'
}}/>
<Tab.Screen name="Cart" component={CartStack} options={{
icon: 'shopping-basket'
}} />
<Tab.Screen name="Profile" component={ProfileStack} options={{
icon: 'person'
}}/>
</Tab.Navigator>
)
}
In first Screen named Home, in HomeStack:
export const HomeStack = () => {
return (
<Stack.Navigator screenOptions={screenOptions} >
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="SectionDrawer" component={SectionDrawer} />
<Stack.Screen name="Detail" component={DetailScreen} />
</Stack.Navigator>
)
}
I calling drawer component in second screen
Drawer
export const SectionDrawer = (props) => {
const { route, navigation } = props
const { list } = route.params
return (
<Drawer.Navigator drawerContentOptions={{ activeBackgroundColor: '#5cbbff', activeTintColor: '#ffffff' }} >
<Drawer.Screen name="Section" component={SectionScreen} options={{ route, navigation }}/>
</Drawer.Navigator>
)
}
And finally in SectionScreen i`m fetcing data with api by id
SectionScreen
export const SectionScreen = (props) => {
console.log(props);
const {route, navigation} = props
const { sectionID } = route.params
const [isLoading, setLoading] = useState(true);
const [productList, setProductList] = useState([]);
useEffect(() => {
fetch('url, {
method: 'GET'
})
.then((response) => response.json())
.then((json) => setProductList(json))
.catch((error) => console.error(error))
.finally(() => setLoading(false));
}, []);
return (
<Content padder>
<Text>section screen.</Text>
<Text>Requested id {sectionID}</Text>
{isLoading ? (
<Spinner />
) : (
<ProductCards list={productList} perLine={2} navigation={navigation}/>
)}
</Content>
)
}
I calling SectionScreen in drawer with this construction:
<CardItem cardBody button onPress={() => {
navigation.dispatch(
CommonActions.navigate({
name: 'SectionDrawer',
params: {
screen: 'SectionScreen',
params: {
title: card.title,
sectionID: card.id
}
},
})
);
}}>
All of this works ... but I need to push in drawer dynamic links fetched from api
I am tried: in Drawer
<Drawer.Navigator drawerContentOptions={{ activeBackgroundColor: '#5cbbff', activeTintColor: '#ffffff' }} >
{list.map((item, index) => <Drawer.Screen name={item.title} component={SectionScreen} key={index}/>)}
</Drawer.Navigator>
and in place where I calling SectionScreen:
<CardItem cardBody button onPress={() => {
navigation.dispatch(
CommonActions.navigate({
name: 'SectionDrawer',
params: {
list,
screen: card.title,
params: {
title: card.title,
sectionID: card.id
}
},
})
);
}}>
After this I see items that I passed in list param,
but when i try to navigate to them, i have error in SectionScreen route.param.id is not defined, and route.params is undefined too.
Also i trying to fetch data that I need in drawer, in drawer component, and draw it with
drawerContent={props => <CustomDrawerContent {...props} />}
and render them with:
{catalogSections.map((item, index) => <DrawerItem label={item.title} focused onPress={() => drawerNavigate(navigation, item)} key={index}/>)}
and it works fine but i cannot select active item from them...
help me please how right to add items to drawer and navigate always to one component with different params

onPress to navigate on a screen not navigating

I am a beginner in React Native and I would like to navigate on a screen when I press the Button of my Navigator screen but it's not working.
Because when I press the Button in TabOneStack.Screen nothing is happening I don't understand, I would like to navigate to TabTwoScreen. I use React Navigation 5.6.1.
const BottomTab = createBottomTabNavigator<BottomTabParamList>();
export default function BottomTabNavigator() {
return (
<BottomTab.Navigator
initialRouteName="TabOne">
<BottomTab.Screen
name="TabOne"
component={TabOneNavigator}
/>
<BottomTab.Screen
name="TabTwo"
component={TabTwoNavigator}
options={{
tabBarLabel: 'Autour de moi',
tabBarIcon: ({ color }) => <TabBarIcon name="ios-navigate" color={color} />,
}}
/>
</BottomTab.Navigator>
);
}
const TabOneStack = createStackNavigator<TabOneParamList>();
function TabOneNavigator() {
return (
<TabOneStack.Navigator>
<TabOneStack.Screen
name="TabOneScreen"
component={TabOneScreen}
options={({ navigation }) => ({
headerTitle: 'Rejoindre', headerRight: () => (
<Button onPress={() => navigation.navigate('TabTwoScreen')}
icon={
<Icon
name='ios-log-in'
type='ionicon'
size={15}
color="white"
/>
}
/>
),
})}
/>
</TabOneStack.Navigator>
);
}
const TabTwoStack = createStackNavigator<TabTwoParamList>();
function TabTwoNavigator() {
return (
<TabTwoStack.Navigator>
<TabTwoStack.Screen
name="TabTwoScreen"
component={TabTwoScreen}
options={{ headerTitle: 'Autour de moi' }}
/>
</TabTwoStack.Navigator>
);
}
Why when I press the Button in TabOneStack.Screen nothing is happening?
Thank you in advance
You need to use props for navigation and you can try this
options={({ navigation }) => ({
headerTitle: 'Rejoindre', headerRight: props => (
<Button onPress={() => props.navigation.navigate('TabTwoScreen')}

Statically setting stack navigator's header title

Suppose I have 2 navigators as follow:
const StackNavigator = () => {
const theme = useTheme();
return (
<Stack.Navigator
initialRouteName="BottomTabs"
headerMode="screen"
screenOptions={{
header: Header,
}}>
<Stack.Screen
name="BottomTabs"
component={BottomTabs}
options={({route, navigation}) => {
console.log('get bottom tab title', route, navigation)
const routeName = route.state
? route.state.routes[route.state.index].name
: 'NOTITLE';
return {headerTitle: routeName};
}}
/>
</Stack.Navigator>
);
};
This Stack navigator loads BottomTabs which is another navigator:
const BottomTabs = props => {
const theme = useTheme();
const tabBarColor = theme.dark
? overlay(6, theme.colors.surface)
: theme.colors.surface;
return (
<Tab.Navigator
initialRouteName="TaskList"
shifting={true}
activeColor={theme.colors.primary}
inactiveColor={color(theme.colors.text)
.alpha(0.6)
.rgb()
.string()}
backBehavior={'initialRoute'}
sceneAnimationEnabled={true}>
<Tab.Screen
name="TaskList"
component={TaskListScreen}
options={{
tabBarIcon: ({focused, color}) => (
<FeatherIcons color={color} name={'check-square'} size={23} />
),
tabBarLabel: 'InboxLabel',
tabBarColor,
title: 'Inbo title',
}}
/>
<Tab.Screen
name="Settings"
component={SettingsScreen}
options={{
tabBarIcon: ({focused, color}) => (
<FeatherIcons color={color} name={'settings'} size={23} />
),
tabBarLabel: 'SeetingsLabel',
tabBarColor,
title: 'Settings title',
}}
/>
</Tab.Navigator>
);
};
I want to change Stack header title based on the screen loaded from BottomTabs. trying to pass the title option to individual screen in BottomTabs didn't work.
How can I change Stack navigator's title based on the screen loaded from the child?
You can customize headerTitle like this:
<Stack.Screen
name="BottomTabs"
component={BottomTabs}
options={({route}) => {
let title;
const routeName = route.state
? route.state.routes[route.state.index].name
: route.params && route.params.screen
? route.params.screen
: 'TaskList';
switch (routeName) {
case 'TaskList':
title = 'Tasks screen';
break;
case 'Settings':
title = 'Settings screen';
break;
default:
return routeName;
}
return {headerTitle: title};
}}
/>
important note: route.state is undefined before any navigate. after that, stack creats it's on navigation state and your screen name prop is available.
Reference