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

多個抽屜

有時候我們想要在同一個畫面中有多個抽屜:一個在左邊,一個在右邊。這可以用 2 種方法達成

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

但有一個問題。當我們在我們的「主畫面」中呼叫 `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>
);
}

在這裡,我們使用 `RightDrawerContext` 傳遞 `openRightDrawer` 函式到「主畫面」。然後我們使用 `openRightDrawer` 來開啟右邊的抽屜。

巢狀兩個抽屜導航器

一種替代方法是在彼此內部巢狀兩個抽屜導航器。不建議這樣做,因為它需要建立一個額外的畫面和更多巢狀結構,這會讓導航和類型檢查變得更冗長。但如果兩個導航器都包含多個畫面,這樣做可能會有用。

在這裡我們有 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>
);
}

但有一個問題。當我們在我們的「主畫面」中呼叫 `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()` 來開啟右邊的抽屜。

總結