You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 13:23:18