多个抽屉
有时我们希望在同一屏幕上有多个抽屉:一个在左侧,一个在右侧。这可以通过 2 种方式实现
- 直接使用
react-native-drawer-layout
(推荐)。 - 通过嵌套 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 />;
}
import * as React from 'react';
import { View } from 'react-native';
import { Drawer } from 'react-native-drawer-layout';
import { useNavigation } from '@react-navigation/native';
import { createDrawerNavigator } from '@react-navigation/drawer';
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 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
对象访问右侧抽屉,因为它不是导航器。
为了解决这个问题,我们需要使用 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 />;
}
import * as React from 'react';
import { View } from 'react-native';
import { Drawer } from 'react-native-drawer-layout';
import { useNavigation } from '@react-navigation/native';
import { createDrawerNavigator } from '@react-navigation/drawer';
import { Button } from '@react-navigation/elements';
const RightDrawerContext = React.createContext();
function HomeScreen() {
const navigation = useNavigation();
const { openRightDrawer } = React.useContext(RightDrawerContext);
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 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
函数到 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 />;
}
import * as React from 'react';
import { View } from 'react-native';
import { createDrawerNavigator } from '@react-navigation/drawer';
import { NavigationContainer, 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 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()} >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 />;
}
import * as React from 'react';
import { Text, View } from 'react-native';
import { createDrawerNavigator } from '@react-navigation/drawer';
import { NavigationContainer, 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 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
prop 中传递 "LeftDrawer"
和 "RightDrawer"
字符串(你可以在这里使用任何字符串)。然后我们使用 navigation.getParent('LeftDrawer').openDrawer()
来打开左侧抽屉,使用 navigation.getParent('RightDrawer').openDrawer()
来打开右侧抽屉。
总结
- 要拥有多个抽屉,你可以结合抽屉导航器直接使用
react-native-drawer-layout
。 drawerPosition
prop 可以用于将抽屉定位在右侧。- 当使用
react-native-drawer-layout
时,控制抽屉的方法可以使用 Context API 传递下去。 - 当嵌套多个导航器时,你可以结合
id
prop 使用navigation.getParent
来引用所需的抽屉。