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

Expo文件路由下实现Drawer Navigation与Bottom Tab共存及隔离路由需求问询

Expo React Native 抽屉导航+文件式路由实现方案

核心方案概述

针对你的需求,采用 Expo Router 文件式路由,通过分组路由区分公开/私有页面,结合动态路由实现抽屉项的独立底部标签导航隔离,以下是分步实现细节:

1. 路由文件结构设计

完全遵循 Expo Router 约定,划分公开(欢迎、认证)和私有(带抽屉+底部标签)路由组,用动态路由处理抽屉项的动态性:

app/
├── (public)/          # 无导航的公开页面
│   ├── welcome.tsx
│   └── auth/
│       ├── login.tsx
│       └── signup.tsx
├── (private)/         # 带抽屉导航的私有路由组
│   ├── _layout.tsx    # 抽屉导航布局
│   ├── [itemId]/      # 动态抽屉项对应的路由容器
│   │   ├── _layout.tsx  # 底部标签导航布局
│   │   ├── profile.tsx
│   │   └── settings.tsx
└── _layout.tsx        # 根布局,处理权限跳转

2. 根布局:权限与路由切换

在根布局中根据认证状态,自动切换公开/私有路由:

import { Stack, useRouter, useSegments } from 'expo-router';
import { useEffect } from 'react';
import { useAuth } from '../contexts/AuthContext'; // 自定义认证状态管理

export default function RootLayout() {
  const { isAuthenticated } = useAuth();
  const segments = useSegments();
  const router = useRouter();

  useEffect(() => {
    const inPublicRoute = segments[0] === '(public)';
    // 未登录且不在公开路由,跳转到欢迎页
    if (!isAuthenticated && !inPublicRoute) {
      router.replace('/(public)/welcome');
    }
    // 已登录且在公开路由,跳转到默认抽屉项
    else if (isAuthenticated && inPublicRoute) {
      router.replace('/(private)/1');
    }
  }, [isAuthenticated, segments]);

  return (
    <Stack screenOptions={{ headerShown: false }}>
      <Stack.Screen name="(public)" />
      <Stack.Screen name="(private)" />
    </Stack>
  );
}

3. 私有路由:动态抽屉导航布局

实现最多100项的动态抽屉菜单,点击项跳转到对应动态路由:

import { Drawer } from 'expo-router/drawer';
import { useState, useEffect } from 'react';
import { View, Text, TouchableOpacity, ScrollView } from 'react-native';

// 生成最多100个动态抽屉项
const generateDrawerItems = () => {
  const items = [];
  for (let i = 1; i <= 100; i++) {
    items.push({ id: i.toString(), label: `Item ${i}` });
  }
  return items;
};

export default function PrivateLayout() {
  const [drawerItems, setDrawerItems] = useState([]);

  useEffect(() => {
    setDrawerItems(generateDrawerItems());
  }, []);

  return (
    <Drawer
      drawerContent={({ navigation }) => (
        <ScrollView style={{ flex: 1, paddingTop: 50, backgroundColor: '#fff' }}>
          {drawerItems.map(item => (
            <TouchableOpacity
              key={item.id}
              onPress={() => navigation.navigate(`(private)/${item.id}`)}
              style={{ padding: 18, borderBottomWidth: 1, borderBottomColor: '#f0f0f0' }}
            >
              <Text style={{ fontSize: 16 }}>{item.label}</Text>
            </TouchableOpacity>
          ))}
        </ScrollView>
      )}
    >
      <Drawer.Screen name="[itemId]" options={{ headerShown: false }} />
    </Drawer>
  );
}

4. 动态抽屉项:独立底部标签导航

每个抽屉项对应独立的底部标签导航,确保导航栈完全隔离:

import { Tabs } from 'expo-router';
import { Ionicons } from '@expo/vector-icons';

export default function ItemTabLayout() {
  return (
    <Tabs screenOptions={{ headerShown: false, tabBarActiveTintColor: '#2196F3' }}>
      <Tabs.Screen
        name="profile"
        options={{
          tabBarLabel: 'Profile',
          tabBarIcon: ({ color, size }) => (
            <Ionicons name="person-circle" size={size} color={color} />
          ),
        }}
      />
      <Tabs.Screen
        name="settings"
        options={{
          tabBarLabel: 'Settings',
          tabBarIcon: ({ color, size }) => (
            <Ionicons name="settings-outline" size={size} color={color} />
          ),
        }}
      />
    </Tabs>
  );
}

5. 页面示例:获取当前抽屉项ID

在Profile/Settings页面中,通过动态参数获取当前抽屉项ID,实现隔离内容:

// app/(private)/[itemId]/profile.tsx
import { Text, View, StyleSheet } from 'react-native';
import { useLocalSearchParams } from 'expo-router';

export default function ProfileScreen() {
  const { itemId } = useLocalSearchParams();
  return (
    <View style={styles.container}>
      <Text style={styles.text}>Profile for Item {itemId}</Text>
    </View>
  );
}

const styles = StyleSheet.create({
  container: { flex: 1, justifyContent: 'center', alignItems: 'center' },
  text: { fontSize: 18, fontWeight: '500' }
});
// app/(private)/[itemId]/settings.tsx
import { Text, View, StyleSheet } from 'react-native';
import { useLocalSearchParams } from 'expo-router';

export default function SettingsScreen() {
  const { itemId } = useLocalSearchParams();
  return (
    <View style={styles.container}>
      <Text style={styles.text}>Settings for Item {itemId}</Text>
    </View>
  );
}

const styles = StyleSheet.create({
  container: { flex: 1, justifyContent: 'center', alignItems: 'center' },
  text: { fontSize: 18, fontWeight: '500' }
});

关键细节说明

  • 动态抽屉项:通过循环生成最多100个项,自定义抽屉内容实现动态渲染
  • 独立导航隔离:每个[itemId]路由下的Tabs拥有独立导航栈,Expo Router自动维护状态隔离
  • 文件式路由合规:完全遵循分组路由、动态路由的命名约定,无需手动配置路由表
  • 权限控制:根布局自动根据认证状态跳转,确保未登录用户无法访问私有路由

内容的提问来源于stack exchange,提问作者sravan ganji

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 05:23:16