跳至主要内容
版本:6.x

多个抽屉

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

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

使用 react-native-drawer-layout

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

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

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

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

const LeftDrawer = createDrawerNavigator();

const LeftDrawerScreen = () => {
return (
<LeftDrawer.Navigator screenOptions={{ drawerPosition: 'left' }}>
<LeftDrawer.Screen name="Home" component={HomeScreen} />
</LeftDrawer.Navigator>
);
};

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>
);
}

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

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

为了解决这个问题,我们需要使用上下文 API 将一个函数传递到控制右侧抽屉。

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

const RightDrawerContext = React.createContext();

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

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

const LeftDrawer = createDrawerNavigator();

const LeftDrawerScreen = () => {
return (
<LeftDrawer.Navigator screenOptions={{ drawerPosition: 'left' }}>
<LeftDrawer.Screen name="Home" component={HomeScreen} />
</LeftDrawer.Navigator>
);
};

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>
);
}

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

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

嵌套 2 个抽屉导航器

另一种方法是将 2 个 抽屉导航器 互相嵌套。这并不推荐,因为它需要创建额外的屏幕和更多嵌套,这会使导航和类型检查变得更加冗长。但这在两个导航器都包含多个屏幕时可能有用。

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

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

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

const LeftDrawer = createDrawerNavigator();

const LeftDrawerScreen = () => {
return (
<LeftDrawer.Navigator screenOptions={{ drawerPosition: 'left' }}>
<LeftDrawer.Screen name="Home" component={HomeScreen} />
</LeftDrawer.Navigator>
);
};

const RightDrawer = createDrawerNavigator();

const RightDrawerScreen = () => {
return (
<RightDrawer.Navigator
screenOptions={{ drawerPosition: 'right', headerShown: false }}
>
<RightDrawer.Screen name="HomeDrawer" component={LeftDrawerScreen} />
</RightDrawer.Navigator>
);
};

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

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

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

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

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

为了自定义抽屉的内容,我们可以使用 drawerContent 属性 传递一个渲染自定义组件的函数。

最终的代码看起来像这样

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

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

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

const LeftDrawer = createDrawerNavigator();

function LeftDrawerScreen() {
return (
<LeftDrawer.Navigator
id="LeftDrawer"
screenOptions={{ drawerPosition: 'left' }}
>
<LeftDrawer.Screen name="Home" component={HomeScreen} />
</LeftDrawer.Navigator>
);
}

const RightDrawer = createDrawerNavigator();

function RightDrawerScreen() {
return (
<RightDrawer.Navigator
id="RightDrawer"
drawerContent={(props) => <RightDrawerContent {...props} />}
screenOptions={{
drawerPosition: 'right',
headerShown: false,
}}
>
<RightDrawer.Screen name="HomeDrawer" component={LeftDrawerScreen} />
</RightDrawer.Navigator>
);
}

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

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

总结