I'm working on a simple app and have now added drawer navigation, but I'm having a weird problem with the drawer that opens again after navigating to the screen from the drawer content.
PlayerZone is a BottomTabNavigation that contains Profile and other screens.
I tried to dispatch close drawer action before and after navigate to screen, also I tried requestAnimationFrame but nothing helps.
how it looks like
DrawerScreens:
<Animated.View style={[styles.stack, style]}>
<Stack.Navigator
screenOptions={{
header: () => null,
}}>
<Stack.Screen name="PlayerZone" component={PlayerZone} />
</Stack.Navigator>
<GameBottomNavigation />
</Animated.View>
Drawer:
<DrawerItem
label="Profile"
style={styles.item}
labelStyle={styles.itemLabel}
onPress={() => {
props.navigation.navigate('PlayerZone', {
screen: 'Profile',
id: null,
});
}}
/>
<DrawerItem
label="Items"
style={styles.item}
labelStyle={styles.itemLabel}
onPress={() => {
props.navigation.navigate('PlayerZone', {
screen: 'Profile',
id: 'some-id',
});
}}
/>
PlayerZone:
<Tab.Navigator
initialRouteName="Profile"
screenOptions={{
header: () => null,
}}>
<Tab.Screen name="Profile" component={Profile} />
{!peeking && <Tab.Screen name="Items" component={Items} />}
</Tab.Navigator>
Items:
import React, {FC} from 'react';
import {BottomTabScreenProps} from '#react-navigation/bottom-tabs';
import Block from '../../components/Block';
import Text from '../../components/Text';
import {PlayerZoneTabParamList} from '.';
type Props = BottomTabScreenProps<PlayerZoneTabParamList, 'Items'>;
const Items: FC<Props> = () => {
return (
<Block middle center>
<Text>Items</Text>
</Block>
);
};
export default Items;
So after some time trying everything I could think of I found solution (kind of). In drawer navigator I had props useLegacyImplementation set to false because I have configured reanimated 2. I removed this props and everything start working properly.
Related
I have a Drawer navigator which is inside a Stack navigator and I'd like to display a header. Currently I can display everything I want however because the header is defined at the Stack level the navigation inside the header is stack level not drawer level which is preventing me from opening the drawer.
Root stack
<Stack.Navigator
initialRouteName={"Splash"}
screenOptions={{}}
component={SplashScreen}
>
{ auth ?
<Stack.Screen name="Drawer" component={DrawerStack} options={({ navigation }) => ({
title: 'My App',
headerLeft: () => (
<HeaderLeft navigation={ navigation } />
),
headerRight: () => (
<HeaderRight navigation={ navigation } />
),
headerTitleAlign: 'center',
headerTintColor: 'white',
headerStyle: {
backgroundColor: '#5742f5'
},
})} />
:
<Stack.Screen name="Auth" component={AuthStack} options={{
headerShown: false
}}/>
}
</Stack.Navigator>
Drawer stack
<Drawer.Navigator options={{
headerShown: true,
headerLeft: () => (
<HeaderLeft navigation={ navigation } />
),
}}>
<Drawer.Screen
name="Conversations"
options={{
title: props.title,
}}
component={ChatListScreen}
/>
<Drawer.Screen
name="ChatRoom"
options={{
drawerLabel: () => null,
title: null,
drawerIcon: () => null
}}
component={ChatRoomScreen}
/>
</Drawer.Navigator>
Note in the drawer navigator the line with headerLeft does nothing and is there to show where I attempted to put it thinking it would work. I did think it might be overlaying the stack one so I commented out the stack one and it didn't work.
HeaderLeft
export default function HeaderLeft ({ navigation }) {
const openMenu = () => {
navigation.toggleDrawer();
}
return (
<View style={styles.header}>
<Icon name='menu' onPress={openMenu} size={28} style={styles.icon} color="white"/>
</View>
)
}
My question is how can I refactor this to enable me to have the HeaderLeft component work to open the drawer. I will be adding more screens so ideally something I don't have to pass to each screen but if that is what works I am good with it too.
Options in DrawerStack not work. I modified it:
<Drawer.Navigator
screenOptions={{
headerLeft: () => <HeaderLeft />,
}}>
// ...
</Drawer.Navigator>
And a little change in HeaderLeft:
import { useNavigation } from '#react-navigation/native';
function HeaderLeft() {
const navigation = useNavigation();
const openMenu = () => {
navigation.toggleDrawer();
};
// render your Button
}
Demo: https://snack.expo.dev/#pqv2210/0d613b
I am in the process of implementing drawer navigation with nested tab navigation within my app.
At present I have two issues that I cannot work out so far and am looking for pointers. I am using React Navigation v6.
Upon clicking any of my tab navigator links I notice that my screen title is always set to "Home". Can this be updated to the current selected tab?
The last clicked tab appears to keep its state when using the drawer links, the journey would look like
Start on home page (Renders home screen fine)
click userprofile tab (Renders user profile page fine)
then click "Book New Class" from within the Drawer, (Renders New Class page fine)
then click "Home" from within the Drawer (This renders the user profile screen)
I have created a snack that will hopefully show the issues I'm facing https://snack.expo.dev/#richlewis14/drawer-and-tab-navigation
DrawerNavigator.js
import React, {useContext} from 'react';
import CustomDrawer from '../components/CustomDrawer.js';
import {BookNewEvent} from '../screens/events/BookNewEvent.js';
import {UserProfile} from '../screens/profile/UserProfile.js';
import BottomTabNavigator from '../navigation/BottomTabNavigator.js';
import {createDrawerNavigator} from '#react-navigation/drawer';
import Ionicons from 'react-native-vector-icons/Ionicons';
const Drawer = createDrawerNavigator();
const DrawerNavigator = ({navigation}) => {
return (
<Drawer.Navigator
drawerContent={props => <CustomDrawer {...props} />}
screenOptions={{
drawerActiveBackgroundColor: '#2B6EB5',
drawerActiveTintColor: '#fff',
drawerInactiveTintColor: '#333',
drawerLabelStyle: {marginLeft: -25, fontSize: 15},
}}>
<Drawer.Screen
name="Home"
component={BottomTabNavigator}
options={{
title: 'Home',
drawerIcon: ({color}) => <Ionicons name="home" size={22} color={color} />,
}}
/>
<Drawer.Screen
name="BookNewEvent"
component={BookNewEvent}
options={{
title: 'Book Class',
drawerIcon: ({color}) => <Ionicons name="calendar-outline" size={22} color={color} />,
}}
/>
</Drawer.Navigator>
);
};
export default DrawerNavigator;
BottomTabNavigator.js
import React, {useContext} from 'react';
import {UserLandingPage} from '../screens/landing/UserLandingPage.js';
import {BookNewEvent} from '../screens/events/BookNewEvent.js';
import {UserProfile} from '../screens/profile/UserProfile.js';
import {createBottomTabNavigator} from '#react-navigation/bottom-tabs';
import {createNativeStackNavigator} from '#react-navigation/native-stack';
import Ionicons from 'react-native-vector-icons/Ionicons';
const Tab = createBottomTabNavigator();
const Stack = createNativeStackNavigator();
const UserHomeStack = () => {
return (
<Stack.Navigator>
<Stack.Screen
name="Home"
component={UserLandingPage}
options={{headerShown: false}}
/>
</Stack.Navigator>
);
};
const BottomTabNavigator = ({navigation}) => {
return (
<Tab.Navigator
screenOptions={{
headerShown: false,
tabBarShowLabel: false,
}}>
<Tab.Screen
name="Home2"
component={UserHomeStack}
options={{
tabBarIcon: () => <Ionicons name="home" size={22} />,
}}
/>
<Tab.Screen
name="UserBookNewEvent"
component={BookNewEvent}
options={{
tabBarIcon: () => <Ionicons name="add-circle" size={22} />,
}}
/>
<Tab.Screen
name="UserProfilePage"
component={UserProfile}
options={{
tabBarIcon: () => <Ionicons name="person" size={22} color="black" />,
}}
/>
</Tab.Navigator>
);
};
export default BottomTabNavigator;
You are nesting a Tab.Navigator inside a Drawer.Navigator. Compare this with the following code snippet from your snack.
<Drawer.Screen
name="Home"
component={BottomTabNavigator}
options={{
title: 'Home',
drawerIcon: ({color}) => <Ionicons name="home" size={22} color={color} />,
}}
/>
This is not a problem but you need to keep in mind that nesting navigators change certain behaviors of your application, e.g.
Each navigator has its own options​.
For example, specifying a title option in a screen nested in a child navigator won't affect the title shown in a parent navigator.
We can fix this by using the code provided in the official documentation of react-native for setting parent screen options based on child navigators state.
Here is what I have done in order to fix your title issue. In BottomTabNavigator.js:
const BottomTabNavigator = ({navigation, route}) => {
function getHeaderTitle(route) {
const routeName = getFocusedRouteNameFromRoute(route) ?? 'Home';
switch (routeName) {
case 'Home2':
return 'Home';
case 'UserBookNewEvent':
return 'Book Class';
case 'UserProfilePage':
return 'Profile';
}
}
React.useLayoutEffect(() => {
navigation.setOptions({ headerTitle: getHeaderTitle(route) });
}, [navigation, route]);
...
Notice that our BottomTabNavigator is a child of the Drawer for the Home-Screen, thus it seems that both navigator navigate to the same screen while updating each others navigation states. This is not possible. We can see this, if we navigate via the Drawer to a different screen, then its title is now updated, however, the screen stays the same.
If it is desired to navigate via the Tab.Navigator if the user is on the Home-screen, such that both navigators stay visible, then this is possible. If the user navigates to a screen, different then the Home- screen via the Drawer, then the Tab.Navigator will vanish.
Thanks to the discussion from the comments, this is acceptable. Solving your second issue can be done with a little hack by setting the prop unmountOnBlur to true for the Home-screen provided in the DrawerNavigator as follows.
<Drawer.Screen
name="Home"
component={BottomTabNavigator}
options={{
title: 'Home',
unmountOnBlur:true,
drawerIcon: ({color}) => <Ionicons name="home" size={22} color={color} />,
}}
/>
Please consider my updated snack for a full working solution.
First I create Bottomtabs and import in Core navigator;
const BottomTabs = createBottomTabNavigator();
function BottomTabNavigator({ navigation, route }) {
return (
<BottomTabs.Navigator>
<BottomTabs.Screen
name="ExploreTab"
component={ExploreNavigator}
/>
<BottomTabs.Screen
name="FormScreen"
component={ProfileNavigator}
options={({ navigation }) => ({
tabBarLabel: "",
tabBarButton: (props) => (
<TouchableOpacity
{...props}
onPress={() => {
console.log({ navigation });
navigation.navigate("FormScreen");
}}
>
<View>
<Icon
type="font-awesome-5"
name="plus"
size={moderateScale(25)}
color={Colors.white}
/>
</View>
</TouchableOpacity>
),
})}
/>
<BottomTabs.Screen
name="ProfileTab"
component={ProfileNavigator}
/>
</BottomTabs.Navigator>
);
}
export default BottomTabNavigator;
The bottom tab navigator is part of the core navigator and the core navigator is the app-level navigator.
function CoreNavigator() {
return (
<CoreStack.Navigator>
<CoreStack.Screen
name="BottomTabNavigator"
component={BottomTabNavigator}
options={{
headerShown: false,
}}
/>
<CoreStack.Screen name="AboutScreen" component={AboutScreen} />
<CoreStack.Screen name="WebScreen" component={WebScreen} />
</CoreStack.Navigator>
);
}
when I click on the bottom tab middle plus button. It should navigate to the form screen FormScreen.
Check image
In simple words below code not working which is present at the Botton tab navigator.
when I click on the bottom plus button Bellow app navigates to ProfileNavigators default screen. I want the app to navigate to FormScreen.
<BottomTabs.Screen
name="FormScreen"
component={ProfileNavigator}
options={({ navigation }) => ({
tabBarLabel: "",
tabBarButton: (props) => (
<TouchableOpacity
{...props}
onPress={() => {
console.log({ navigation });
navigation.navigate("FormScreen");
}}
>
<View>
<Icon
type="font-awesome-5"
name="plus"
size={moderateScale(25)}
color={Colors.white}
/>
</View>
</TouchableOpacity>
),
})}
/>
Try to use:
navigation.reset({
index: 0,
routes: [{ name: 'FormScreen' }],
});
Instead of navigation.navigate("FormScreen");
I am having this issue where a custom made TabBarIcon is not showing fully. I have read the docs, github to try and find a solution for this.
What I could make out was that nesting a BottomTabsNavigator in a DrawerNavigator might have repercussions, please correct me if I'm wrong.
This is how my Navigation Container would look like
AppNavigator
Authenticated ? DrawerNavigator : AuthNavigator
// Inside DrawerNavigator:
DrawerNavigator
BottomTabsNavigator
Rest of the screens
An image for illustration of my problem:
AppNavigator.js:
// AppNav uses StackNavigation
<NavigationContainer>
<Navigator headerMode='none'>
{authenticated && !isLoading ? (
<Screen name='DrawerNavigator' component={DrawerNavigator} />
) : !authenticated && !isLoading ? (
<Screen name='AuthNavigator' component={AuthNavigator} />
) : (
<Screen name='Loading' component={LoadingScreen} />
)}
</Navigator>
</NavigationContainer>
DrawerNavigator.js:
// I am using a UI library, UI kitten for the Drawer. But I don't think this is the cause
const { Navigator, Screen } = createDrawerNavigator();
const DrawerNavigator = () => (
<Navigator
drawerContent={props => <DrawerContent {...props} />}
initialRouteName='BottomTabsNavigator'
>
<Screen name='BottomTabsNavigator' component={BottomTabsNavigator} />
<Screen name='ChangePassword' component={ChangePasswordScreen} />
<Screen name='AuthNavigator' component={AuthNavigator} />
<Screen name='UserProfile' component={UserProfileScreen} />
<Screen name='EditProfile' component={EditProfileScreen} />
<Screen name='ActivityFeed' component={ActivityFeedScreen} />
</Navigator>
);
BottomTabsNavigator.js:
const BottomTabs = createBottomTabNavigator();
const BottomTabsNavigator = ({ navigation }) => {
return (
<BottomTabs.Navigator
tabBarOptions={{
showLabel: false,
activeTintColor: '#407BFF'
}}
initialRouteName='Home'
>
<BottomTabs.Screen
name='Home'
component={MainAppScreen}
options={{
tabBarIcon: Some Icon
}}
/>
<BottomTabs.Screen
name='TeamUp'
component={TeamUpScreen}
options={{
tabBarIcon: () => <TeamUpBottomTab navigation={navigation} /> // Custom made 'Icon'
}}
/>
<BottomTabs.Screen
name='ChatOverview'
component={ChatOverviewScreen}
options={{
tabBarIcon: Some Icon
}}
/>
</BottomTabs.Navigator>
);
};
The custom made Icon is as such:
import { Platform, Pressable, StyleSheet, Animated, Image } from 'react-native';
<Pressable onPress={pressHandler} style={styles.teamUp}>
<Animated.View style={[{ transform: [{ scale: animatedScale }] }]}>
<Image
style={styles.logo}
source={require('../../assets/images/orbital-logo.png')}
/>
</Animated.View>
</Pressable>
Been stuck for a few days, and searching for a solution isn't helping much.
Appreciate any help, thank you
I have a Drawer Navigator with some Screens. One of the Screens is a TabNavigator. Now I want to implement a HamburgerMenu-Button to open the drawer in all of the Tab-Navigator Screens.
Where do I implement that Button? In the TabNavigator or its child Screens or in the Drawer Screens?
Here are a few Code Snippets:
DrawerNavigator:
export default class DrawerMenu extends React.Component {
render() {
return (
<Drawer.Navigator initialRouteName='Home'>
<Drawer.Screen name='Home' component={TabNavigator} />
<Drawer.Screen name='About Us' component={AboutUsScreen} />
<Drawer.Screen name='About the App' component={AboutTheAppScreen} />
<Drawer.Screen name='Impressum' component={ImpressumScreen} />
</Drawer.Navigator>
);
}
}
TabNavigator:
const Tab = createBottomTabNavigator();
const TabNavigator = () => {
return (
<Tab.Navigator tabBar={(props) => <TabBar {...props}></TabBar>}>
<Tab.Screen
name='Tab1'
component={Tab1}
initialParams={{ icon: 'info' }}
></Tab.Screen>
<Tab.Screen
name='Tab2'
component={Tab2}
initialParams={{ icon: 'circle-o-notch' }}
></Tab.Screen>
<Tab.Screen
name='Tab3'
component={Tab3}
initialParams={{ icon: 'home' }}
></Tab.Screen>
<Tab.Screen
name='Tab4'
component={Tab4}
initialParams={{ icon: 'glass' }}
></Tab.Screen>
<Tab.Screen
name='Tab5'
component={Tab5}
initialParams={{ icon: 'road' }}
></Tab.Screen>
</Tab.Navigator>
);
};
single Screen in TabNavigator:
const Tab1 = () => {
return (
<View style={styles.container}>
<Text style={styles.text}>Tab1</Text>
</View>
);
};
Any help is appreciated.
In the end it was an easy answer. We just had to give through the props. (I think)
single Screen in TabNavigator:
const Tab1 = ({navigation}) => {
return (
<View style={styles.container}>
<Text style={styles.text}>Tab1</Text>
</View>
);
};
That way we could easily use the navigation.openDrawer function in an onPress event.