Reputation: 849
In my react native app I have a stack navigator nested inside a drawer navigator. I want the drawer to be disabled in the stack navigator pages. I'm using react navigation 6.
In the docs (https://reactnavigation.org/docs/drawer-navigator/#options) I see there are two options for this: gestureEnabled
and swipeEnabled
. But these can only be used in drawer screens, not in stack screens like my case.
My code is as following:
const Stack = createNativeStackNavigator<RootStackParamList>();
const Drawer = createDrawerNavigator<RootTabParamList>();
const loginStack = () => (
<Stack.Navigator>
<Stack.Screen name="LandingScreen" component={LandingScreen} options={{ headerShown: false }} />
<Stack.Screen name="LoginScreen" component={LoginScreen} options={{ headerShown: false }} />
<Stack.Screen
name="RegisterScreen"
component={RegisterScreen}
options={{ headerShown: false }}
/>
</Stack.Navigator>
);
return (
<NavigationContainer>
<Drawer.Navigator
screenOptions={{
drawerStyle: { backgroundColor: 'white' },
drawerPosition: 'right',
}}
>
{!user ? (
<Drawer.Screen
name="PublicStack"
component={loginStack}
// options={{headerShown: false}}
options={({ route }) => {
const routeName = getFocusedRouteNameFromRoute(route);
if (
routeName === 'LandingScreen' ||
routeName === 'LoginScreen' ||
routeName === 'RegisterScreen'
)
return { swipeEnabled: false, gestureEnabled: false };
return { swipeEnabled: true, gestureEnabled: true };
}}
/>
) : (
<>
<Drawer.Screen
name="Search cocktails"
component={HomeScreen}
options={{ header: () => <Header /> }}
/>
<Drawer.Screen
name="Profile"
component={ProfileScreen}
initialParams={{ userParam: null }}
options={{ header: () => <Header /> }}
/>
<Drawer.Screen
name="Publish a recipe"
component={PublishRecipeScreen}
options={{ header: () => <Header /> }}
/>
<Drawer.Screen
name="Favorites"
component={FavoritesScreen}
options={{ header: () => <Header /> }}
/>
<Drawer.Screen
name="Published recipes"
component={PublishedRecipesScreen}
options={{ header: () => <Header /> }}
/>
<Drawer.Screen
name="Log out"
component={CustomDrawerContent}
options={{ header: () => <Header /> }}
/>
<Drawer.Screen
name="CocktailDetailScreen"
component={CocktailDetailScreen}
options={{
header: () => <Header />,
drawerLabel: () => null,
title: undefined,
}}
/>
</>
)}
</Drawer.Navigator>
</NavigationContainer>
);
I've tried setting the mentioned options directly on the loginStack drawer screen, like:
<Drawer.Screen
name='PublicStack'
component={loginStack}
options={{swipeEnabled: false, gestureEnabled: false}}}
/>
But didn't work.
I've also seen this answer (How to disable drawer inside Stack Navigator nested inside Drawer Navigator?) and tried to implement something similar (what my code looks like right now) but still didn't work.
Full code can be found here: https://github.com/coccagerman/mixr
Thanks!
Upvotes: 5
Views: 1410
Reputation: 319
Francisco's answer is working but it is very laggy and the only solution I found was as follows:
const drawerNavigator = () => {
<Drawer.Navigator>
<Drawer.Screen name="DrawerScreen1" component={DrawerScreen1} />
<Drawer.Screen name="DrawerScreen2" component={DrawerScreen2} />
</Drawer.Navigator>
};
<Stack.Navigator>
<Stack.Screen name='DrawerNavigator' children={drawerNavigator} options={{ headerShown: false }} />
<Stack.Screen name='StackScreen1' component={StackScreen1} />
<Stack.Screen name='StackScreen2' component={StackScreen2} />
<Stack.Screen name='StackScreen3' component={StackScreen3} />
</Stack.Navigator>
Basically you insert the Drawer Navigator inside a stack and remove its header.
The downside of that is you have all sub screens (stack screens) listed one below another and grouped in separate Stack Navigators, as it is not a good idea to nest the same type of navigator in one another (e.g. one Stack.Navigator inside another Stack.Navigator).
UPDATE: I found a better solution where I have a lot more control on it and looks much better and logical.
I have screenOptions:{{headerShown: false}}
on the Drawer.Navigator and then I manually add the hamburger icon where I need it
<Stack.Screen
name='StackScreen1'
component={StackScreenComponent1}
options={({ navigation }) =>
({
headerLeft: () => {
<TouchableOpacity onPress={() => navigation.openDrawer()}>
<Icon name='menu' />
</TouchableOpacity>
}
} as NativeStackNavigationOptions)}
/>
This way I can also control the icon of the hamburger menu, the color, the position (left by default or right if I have a 'right to left' language), etc.
Upvotes: 0
Reputation: 1675
In my case, I only want to have swipe enabled for the first screen in the stack navigator that's nested in the drawer navigator.
I span a group around the drawer screens, get the focused route name and only enable swipe if it's the first route of the stack navigator.
<Drawer.Group
screenOptions={({ route }) => ({
swipeEnabled: getFocusedRouteNameFromRoute(route) === 'NameOfFirstScreenInStack',
})}
>
{..}
</Drawer.Group>
getFocuesRouteNameFromRoute
isn't well documented but at least there's a guide working with it.
Upvotes: 0
Reputation: 327
I was stuck with the same thing these days. I didn't find a solution to use getFocusedRouteNameFromRoute(route)
and took a different approach.
The first thing is block the Drawer in the whole app:
<Drawer.Navigator screenOptions = {{ swipeEnabled: false }}>
<Drawer.Screen name="Screen1" component={StackScreen1} />
<Drawer.Screen name="Screen2" component={StackScreen2} />
</Drawer.Navigator>
Then, you enable the Drawer on the screens you need, like this:
useFocusEffect(
useCallback((() => {
// From a Stack screen, the Drawer is accessed.
const parent = navigation.getParent()
parent?.setOptions({ swipeEnabled: true })
// It returns to the initial state.
return () => parent?.setOptions({ swipeEnabled: false })
}, [navigation])
)
In case you have to enable the Drawer on many screens, it can be done the other way around. Enable the Drawer in the whole app, and block it only in the desired ones.
I know that maybe it's not the best solution but I hope it helps you. Saludos!
Upvotes: 5