Expo Router实现带认证的抽屉嵌套栈导航方案咨询
Expo Router 实现双导航逻辑方案
一、核心目录结构
先给出可运行的目录结构,这是实现的基础:
app/ ├── (auth)/ # 未登录路由分组 │ ├── _layout.tsx # 未登录栈导航布局 │ ├── index.tsx # 未登录主页面 │ ├── page1.tsx # 跳转目标页面1 │ └── page2.tsx # 跳转目标页面2 ├── (drawer)/ # 登录后路由分组 │ ├── _layout.tsx # 抽屉导航根布局 │ ├── home/ # 首页嵌套栈 │ │ ├── _layout.tsx │ │ ├── index.tsx │ │ └── detail.tsx # 首页详情页 │ ├── about/ # 关于页嵌套栈 │ │ ├── _layout.tsx │ │ ├── index.tsx │ │ └── team.tsx # 关于子页面 │ └── settings/ # 设置页嵌套栈 │ ├── _layout.tsx │ ├── index.tsx │ └── account.tsx # 设置子页面 ├── _layout.tsx # 根布局,负责根据登录状态切换导航 └── context/ └── AuthContext.tsx # 全局登录状态管理
二、全局登录状态管理
实现一个AuthContext来共享登录状态,方便各组件判断切换:
// app/context/AuthContext.tsx import { createContext, useContext, useState, ReactNode } from 'react'; type AuthContextType = { isLoggedIn: boolean; setIsLoggedIn: (value: boolean) => void; }; const AuthContext = createContext<AuthContextType | undefined>(undefined); export const AuthProvider = ({ children }: { children: ReactNode }) => { const [isLoggedIn, setIsLoggedIn] = useState(false); return ( <AuthContext.Provider value={{ isLoggedIn, setIsLoggedIn }}> {children} </AuthContext.Provider> ); }; export const useAuth = () => { const context = useContext(AuthContext); if (!context) throw new Error('useAuth 必须在 AuthProvider 内部使用'); return context; };
三、根布局:导航切换核心逻辑
根布局根据登录状态,自动渲染对应路由分组:
// app/_layout.tsx import { AuthProvider, useAuth } from './context/AuthContext'; import { Slot } from 'expo-router'; const RootNavigator = () => { const { isLoggedIn } = useAuth(); // 根据登录状态指定初始路由分组 return <Slot initialRouteName={isLoggedIn ? '(drawer)' : '(auth)'} />; }; export default function RootLayout() { return ( <AuthProvider> <RootNavigator /> </AuthProvider> ); }
四、未登录栈导航实现
未登录栈布局
// app/(auth)/_layout.tsx import { Stack } from 'expo-router/stack'; export default function AuthLayout() { return ( <Stack> <Stack.Screen name="index" options={{ title: '未登录首页' }} /> <Stack.Screen name="page1" options={{ title: '页面1' }} /> <Stack.Screen name="page2" options={{ title: '页面2' }} /> </Stack> ); }
未登录主页面(带跳转按钮)
// app/(auth)/index.tsx import { Button, View, StyleSheet } from 'react-native'; import { useRouter } from 'expo-router'; import { useAuth } from '../context/AuthContext'; export default function AuthHome() { const router = useRouter(); const { setIsLoggedIn } = useAuth(); return ( <View style={styles.container}> <Button title="跳转到页面1" onPress={() => router.push('/(auth)/page1')} /> <Button title="跳转到页面2" onPress={() => router.push('/(auth)/page2')} /> <Button title="模拟登录" onPress={() => setIsLoggedIn(true)} /> </View> ); } const styles = StyleSheet.create({ container: { flex: 1, justifyContent: 'center', gap: 16, padding: 20, }, });
五、登录后抽屉导航实现
抽屉根布局
// app/(drawer)/_layout.tsx import { Drawer } from 'expo-router/drawer'; import { DrawerToggleButton } from '@react-navigation/drawer'; export default function DrawerLayout() { return ( <Drawer> <Drawer.Screen name="home" options={{ title: '首页', headerLeft: () => <DrawerToggleButton />, }} /> <Drawer.Screen name="about" options={{ title: '关于', headerLeft: () => <DrawerToggleButton />, }} /> <Drawer.Screen name="settings" options={{ title: '设置', headerLeft: () => <DrawerToggleButton />, }} /> </Drawer> ); }
首页嵌套栈布局(其他顶级页面同理)
// app/(drawer)/home/_layout.tsx import { Stack } from 'expo-router/stack'; import { DrawerToggleButton } from '@react-navigation/drawer'; export default function HomeStack() { return ( <Stack> <Stack.Screen name="index" options={{ title: '首页', headerLeft: () => <DrawerToggleButton />, }} /> <Stack.Screen name="detail" options={{ title: '首页详情', // 详情页也支持触发抽屉 headerLeft: () => <DrawerToggleButton />, }} /> </Stack> ); }
首页主页面(带退出登录按钮)
// app/(drawer)/home/index.tsx import { Button, View, StyleSheet } from 'react-native'; import { useRouter } from 'expo-router'; import { useAuth } from '../../../context/AuthContext'; export default function Home() { const router = useRouter(); const { setIsLoggedIn } = useAuth(); return ( <View style={styles.container}> <Button title="查看首页详情" onPress={() => router.push('/(drawer)/home/detail')} /> <Button title="模拟退出登录" onPress={() => setIsLoggedIn(false)} /> </View> ); } const styles = StyleSheet.create({ container: { flex: 1, justifyContent: 'center', gap: 16, padding: 20, }, });
六、关键注意事项
- 所有登录后的页面(包括嵌套栈的子页面)都添加了
DrawerToggleButton,确保任意页面都能调出抽屉。 - 抽屉仅配置了
home、about、settings三个顶级路由,自然只显示这三个选项。 - 登录状态切换时,Expo Router会自动切换路由分组,无需手动处理页面跳转。
- 如果需要持久化登录状态,可在
AuthContext中集成AsyncStorage,将isLoggedIn状态存储到本地。
内容的提问来源于stack exchange,提问作者dmathisen
相关产品推荐
相关产品推荐

