React Navigation - React Native - 如何在 Drawer Navigator 嵌套的 Stack Navigator 中阻止抽屉导航栏?

4
在我的React Native应用程序中,我有一个嵌套在抽屉式导航器内的堆栈导航器。我希望在堆栈导航器页面中禁用抽屉式导航器。我正在使用React Navigation 6。
在文档(https://reactnavigation.org/docs/drawer-navigator/#options)中,我看到有两个选项:gestureEnabledswipeEnabled。但是这些只能在抽屉式屏幕中使用,而不能在像我的情况下的堆栈屏幕中使用。
我的代码如下:
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>
);

我尝试直接在登录堆栈抽屉屏幕上设置提到的选项,例如:

<Drawer.Screen
  name='PublicStack'
  component={loginStack}
  options={{swipeEnabled: false, gestureEnabled: false}}} 
/>

但是没有起作用。

我也看到了这个答案(如何在Drawer Navigator内嵌套的Stack Navigator中禁用抽屉?),并尝试实现类似的东西(现在我的代码是什么样子),但仍然没有起作用。

完整的代码可以在这里找到:https://github.com/coccagerman/mixr

谢谢!

3个回答

5

最近我一直陷入同样的困境中。我无法找到使用 getFocusedRouteNameFromRoute(route) 的解决方法,因此采取了不同的方法。

第一件事是在整个应用程序中封锁抽屉菜单:

      <Drawer.Navigator screenOptions = {{ swipeEnabled: false }}>
        <Drawer.Screen name="Screen1" component={StackScreen1} />
        <Drawer.Screen name="Screen2" component={StackScreen2} />
      </Drawer.Navigator>

然后,您需要在需要的屏幕上启用抽屉,如下所示:

      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])
      )

如果你需要在许多屏幕上启用抽屉式导航,可以使用另一种方法。在整个应用程序中启用抽屉式导航,并仅在所需的屏幕上阻止它。

我知道这可能不是最好的解决方案,但我希望它能帮到你。问候!


看起来像是一个hack,我正在尝试找到更好的方法。 - evgenii
你找到更好的方法了吗? - Alix Humbert
不,我目前还在这样使用它 :/ - Francisco Gallo M.

0
在我的情况下,我只想在抽屉式导航器中嵌套的堆栈导航器的第一个屏幕启用滑动功能。
我将一个组包围在抽屉屏幕周围,获取焦点路由名称,并仅在堆栈导航器的第一个路由时启用滑动。
<Drawer.Group
  screenOptions={({ route }) => ({
    swipeEnabled: getFocusedRouteNameFromRoute(route) === 'NameOfFirstScreenInStack',
  })}
>
  {..}
</Drawer.Group>

getFocuesRouteNameFromRoute 没有很好的文档说明,但至少有一个指南可以使用它。


0

Francisco的答案是可行的,但是非常卡顿,我找到的唯一解决方案如下:

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>

基本上,您需要将抽屉式导航器插入到堆栈中并删除其标题。

缺点是,您所有的子屏幕(堆栈屏幕)都会列在下面,并分组在单独的堆栈导航器中,因为在彼此嵌套相同类型的导航器(例如一个Stack.Navigator内部嵌套另一个Stack.Navigator)不是一个好主意。

更新:我找到了更好的解决方案,我可以更好地控制它,看起来更好,更合乎逻辑。

我在Drawer.Navigator上有screenOptions:{{headerShown: false}},然后我手动添加需要的汉堡图标。

<Stack.Screen
  name='StackScreen1'
  component={StackScreenComponent1}
  options={({ navigation }) => 
    ({
      headerLeft: () => {
      <TouchableOpacity onPress={() => navigation.openDrawer()}>
        <Icon name='menu' />
      </TouchableOpacity>
      }
    } as NativeStackNavigationOptions)}
/>

这样我也可以控制汉堡菜单的图标,颜色,位置(默认为左侧或者从右至左的语言),等等。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接