React navigation - react native - How to block drawer in Stack Navigator nested inside Drawer Navigator?

Viewed 516

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!

1 Answers

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!

Related