跳到主要内容
版本:7.x

多个抽屉

有时我们希望在同一屏幕上有多个抽屉:一个在左侧,一个在右侧。这可以通过 2 种方式实现

  1. 直接使用 react-native-drawer-layout(推荐)。
  2. 通过嵌套 2 个 抽屉导航器

使用 react-native-drawer-layout

当我们有多个抽屉时,只有一个抽屉显示屏幕列表。第二个抽屉通常用于显示一些附加信息,例如用户列表等。

在这种情况下,我们可以直接使用 react-native-drawer-layout 来渲染第二个抽屉。抽屉导航器将用于渲染第一个抽屉,并且可以嵌套在第二个抽屉内

import * as React from 'react';
import { View } from 'react-native';
import { Drawer } from 'react-native-drawer-layout';
import { createDrawerNavigator } from '@react-navigation/drawer';
import {
createStaticNavigation,
useNavigation,
} from '@react-navigation/native';
import { Button } from '@react-navigation/elements';

function HomeScreen() {
const navigation = useNavigation();

return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Button onPress={() => navigation.openDrawer()}>Open drawer</Button>
</View>
);
}

const LeftDrawerScreen = createDrawerNavigator({
screenOptions: {
drawerPosition: 'left',
},
screens: {
Home: HomeScreen,
},
});

function RightDrawerScreen() {
const [rightDrawerOpen, setRightDrawerOpen] = React.useState(false);

return (
<Drawer
open={rightDrawerOpen}
onOpen={() => setRightDrawerOpen(true)}
onClose={() => setRightDrawerOpen(false)}
drawerPosition="right"
renderDrawerContent={() => <>{/* Right drawer content */}</>}
>
<LeftDrawerScreen />
</Drawer>
);
}

const Navigation = createStaticNavigation(RightDrawerScreen);

export default function App() {
return <Navigation />;
}

但是有一个问题。当我们在 HomeScreen 中调用 navigation.openDrawer() 时,它总是打开左侧抽屉。我们无法通过 navigation 对象访问右侧抽屉,因为它不是导航器。

为了解决这个问题,我们需要使用 Context API 来传递一个函数来控制右侧抽屉

import * as React from 'react';
import { View } from 'react-native';
import { Drawer } from 'react-native-drawer-layout';
import { createDrawerNavigator } from '@react-navigation/drawer';
import {
useNavigation,
createStaticNavigation,
} from '@react-navigation/native';
import { Button } from '@react-navigation/elements';

const RightDrawerContext = React.createContext();

function HomeScreen() {
const { openRightDrawer } = React.useContext(RightDrawerContext);
const navigation = useNavigation();

return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Button onPress={() => navigation.openDrawer()}>Open left drawer</Button>
<Button onPress={() => openRightDrawer()}>Open right drawer</Button>
</View>
);
}

const LeftDrawerScreen = createDrawerNavigator({
screenOptions: {
drawerPosition: 'left',
},
screens: {
Home: HomeScreen,
},
});

function RightDrawerScreen() {
const [rightDrawerOpen, setRightDrawerOpen] = React.useState(false);

const value = React.useMemo(
() => ({
openRightDrawer: () => setRightDrawerOpen(true),
closeRightDrawer: () => setRightDrawerOpen(false),
}),
[]
);

return (
<Drawer
open={rightDrawerOpen}
onOpen={() => setRightDrawerOpen(true)}
onClose={() => setRightDrawerOpen(false)}
drawerPosition="right"
renderDrawerContent={() => <>{/* Right drawer content */}</>}
>
<RightDrawerContext.Provider value={value}>
<LeftDrawerScreen />
</RightDrawerContext.Provider>
</Drawer>
);
}

const Navigation = createStaticNavigation(RightDrawerScreen);

export default function App() {
return <Navigation />;
}

在这里,我们使用 RightDrawerContext 来传递 openRightDrawer 函数到 HomeScreen。然后我们使用 openRightDrawer 来打开右侧抽屉。

嵌套 2 个抽屉导航器

另一种方法是将 2 个 抽屉导航器 相互嵌套。不建议这样做,因为它需要创建额外的屏幕和更多的嵌套 - 这会使导航和类型检查更加冗长。但是,如果两个导航器都包含多个屏幕,这可能会很有用。

这里我们有 2 个相互嵌套的抽屉导航器,一个位于左侧,另一个位于右侧

import * as React from 'react';
import { View } from 'react-native';
import { createDrawerNavigator } from '@react-navigation/drawer';
import {
createStaticNavigation,
useNavigation,
} from '@react-navigation/native';
import { Button } from '@react-navigation/elements';

function HomeScreen() {
const navigation = useNavigation();

return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Button onPress={() => navigation.openDrawer()}>Open drawer</Button>
</View>
);
}

const LeftDrawerScreen = createDrawerNavigator({
screenOptions: {
drawerPosition: 'left',
},
screens: {
Home: HomeScreen,
},
});

const RightDrawerScreen = createDrawerNavigator({
screenOptions: {
drawerPosition: 'right',
headerShown: false,
},
screens: {
HomeDrawer: LeftDrawerScreen,
},
});

const Navigation = createStaticNavigation(RightDrawerScreen);

export default function App() {
return <Navigation />;
}
Snack 上尝试

但是有一个问题。当我们在 HomeScreen 中调用 navigation.openDrawer() 时,它总是打开左侧抽屉,因为它是屏幕的直接父级。

为了解决这个问题,我们需要使用 navigation.getParent 来引用右侧抽屉,它是左侧抽屉的父级。所以我们的代码看起来像

<Button onPress={() => navigation.openDrawer()} >Open left drawer</Button>
<Button onPress={() => navigation.getParent().openDrawer()}>Open right drawer</Button>

但是,这意味着我们的按钮需要了解父导航器,这并不理想。如果我们的按钮进一步嵌套在其他导航器中,则需要多次调用 getParent()。为了解决这个问题,我们可以使用 id prop 来标识父导航器。

要自定义抽屉的内容,我们可以使用 drawerContent prop 来传入一个函数,该函数渲染一个自定义组件。

最终代码如下所示

import * as React from 'react';
import { Text, View } from 'react-native';
import { createDrawerNavigator } from '@react-navigation/drawer';
import {
createStaticNavigation,
useNavigation,
} from '@react-navigation/native';
import { Button } from '@react-navigation/elements';

function HomeScreen() {
const navigation = useNavigation();

return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Button onPress={() => navigation.getParent('LeftDrawer').openDrawer()}>
Open left drawer
</Button>
<Button onPress={() => navigation.getParent('RightDrawer').openDrawer()}>
Open right drawer
</Button>
</View>
);
}

function RightDrawerContent() {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>This is the right drawer</Text>
</View>
);
}

const LeftDrawerScreen = createDrawerNavigator({
id: 'LeftDrawer',
screenOptions: {
drawerPosition: 'left',
},
screens: {
Home: HomeScreen,
},
});

const RightDrawerScreen = createDrawerNavigator({
id: 'RightDrawer',
drawerContent: (props) => <RightDrawerContent {...props} />,
screenOptions: {
drawerPosition: 'right',
headerShown: false,
},
screens: {
HomeDrawer: LeftDrawerScreen,
},
});

const Navigation = createStaticNavigation(RightDrawerScreen);

export default function App() {
return <Navigation />;
}
Snack 上尝试

在这里,我们在抽屉导航器的 id prop 中传递 "LeftDrawer""RightDrawer" 字符串(你可以在这里使用任何字符串)。然后我们使用 navigation.getParent('LeftDrawer').openDrawer() 来打开左侧抽屉,使用 navigation.getParent('RightDrawer').openDrawer() 来打开右侧抽屉。

总结