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

如何用Flutter GoRouter实现带认证页的嵌套导航?

问题与解决方案

问题概述

我开发的Flutter App启动后先进入Splash页,随后跳转至登录页面,用户登录完成后进入类似Instagram的首页(带4个底部标签页的嵌套导航结构,切换标签需保留对应导航状态)。当前已配置MaterialApp.router使用GoRouter,包含认证页面及StatefulShellRoute实现的嵌套导航分支,目前能从Splash页跳转至认证页面,但不知道如何导航到带底部导航栏的嵌套首页,询问GoRouter是否能实现该需求。

解决方案:完全可以用GoRouter实现

GoRouter的StatefulShellRoute.indexedStack正是为这种带底部导航的嵌套场景设计的,只需修正现有路由配置的问题并使用正确的导航方法即可:

1. 修复路由配置的核心问题

你的代码里存在重复定义StatefulShellRoute和**分支路径重复(多个分支用了/c)**的错误,这会导致路由冲突,先修正:

修正后的appRouter代码

class AppRoutes {
  // auth
  static const splash = '/';
  static const login = '/login';
  static const createAcc = '/createAcc';
  static const join = '/join';
  static const createDrivingShool = '/createDrivingShool';

  // 嵌套导航分支路径
  static const lessonHome = '/lessonHome';
  static const studentList = '/studentList';
  static const notifications = '/notifications';
  static const schoolSettings = '/schoolSettings';
}

// 定义导航键
final _rootNavKey = GlobalKey<NavigatorState>();
final _shellNavLessonKey = GlobalKey<NavigatorState>();
final _shellNavStudentKey = GlobalKey<NavigatorState>();
final _shellNavNotificationKey = GlobalKey<NavigatorState>();
final _shellNavSchoolKey = GlobalKey<NavigatorState>();

final appRouter = GoRouter(
  navigatorKey: _rootNavKey,
  initialLocation: '/',
  routes: [
    // 认证相关路由
    GoRoute(
      path: AppRoutes.splash,
      builder: (context, state) => const Splash(),
    ),
    GoRoute(
      path: AppRoutes.login,
      builder: (context, state) => const LoginView(),
    ),
    GoRoute(
      path: AppRoutes.createAcc,
      builder: (context, state) => const CreateAccountView(),
    ),
    GoRoute(
      path: AppRoutes.join,
      builder: (context, state) => const JoinDrivingSchoolView(),
    ),
    GoRoute(
      path: AppRoutes.createDrivingShool,
      builder: (context, state) => const CreateDrivingSchoolView(),
    ),

    // 嵌套导航主路由(只保留一个)
    StatefulShellRoute.indexedStack(
      builder: (context, state, navigationShell) =>
          MainNestedNavigationHome(navigationShell: navigationShell),
      parentNavigatorKey: _rootNavKey,
      branches: [
        StatefulShellBranch(
          navigatorKey: _shellNavLessonKey,
          routes: [
            GoRoute(
              path: AppRoutes.lessonHome,
              pageBuilder: (context, state) => const NoTransitionPage(
                child: InstHome(),
              ),
              routes: [
                // 子路由示例
                GoRoute(
                  path: 'detail',
                  builder: (context, state) => const InstHome(),
                ),
              ],
            ),
          ],
        ),
        StatefulShellBranch(
          navigatorKey: _shellNavStudentKey,
          routes: [
            GoRoute(
              path: AppRoutes.studentList,
              pageBuilder: (context, state) => const NoTransitionPage(
                child: InstStudentListView(),
              ),
            ),
          ],
        ),
        StatefulShellBranch(
          navigatorKey: _shellNavNotificationKey,
          routes: [
            GoRoute(
              path: AppRoutes.notifications,
              pageBuilder: (context, state) => const NoTransitionPage(
                child: InstNotificationsView(),
              ),
            ),
          ],
        ),
        StatefulShellBranch(
          navigatorKey: _shellNavSchoolKey,
          routes: [
            GoRoute(
              path: AppRoutes.schoolSettings,
              pageBuilder: (context, state) => const NoTransitionPage(
                child: SchoolSettingsView(),
              ),
            ),
          ],
        ),
      ],
    ),
  ],
);

2. 登录完成后导航到嵌套首页

在登录页面的登录成功回调中,使用context.go()跳转到第一个分支的初始路径(比如AppRoutes.lessonHome),这样会直接进入带底部导航的嵌套首页,同时清空之前的认证页面栈:

// 登录成功后的动作示例
void onLoginSuccess(BuildContext context) {
  // 跳转到嵌套首页的第一个标签页
  context.go(AppRoutes.lessonHome);
}

3. 保持底部导航的状态

你的MainNestedNavigationHome代码已经正确实现了分支切换的逻辑,navigationShell.goBranch(index)会自动保留每个分支的导航状态,无需额外修改:

class MainNestedNavigationHome extends StatelessWidget {
  const MainNestedNavigationHome({
    Key? key,
    required this.navigationShell,
  }) : super(key: key ?? const ValueKey('ScaffoldWithNestedNavigation'));
  final StatefulNavigationShell navigationShell;

  void _goBranch(int index) {
    navigationShell.goBranch(index);
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: navigationShell,
      bottomNavigationBar: PrimaryBottomAppBar(
        onTap: _goBranch,
      ),
    );
  }
}

关键说明

  • StatefulShellRoute.indexedStack会用IndexedStack管理各个分支的导航栈,切换标签时不会销毁页面,自动保留状态
  • 使用context.go()而非context.push()是为了避免认证页面留在栈中,确保用户按返回键不会回到登录页
  • 每个分支必须有唯一的路径,不能重复,否则会导致路由匹配错误

内容的提问来源于stack exchange,提问作者Shehan DMG

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 12:35:40