如何用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
相关产品推荐
相关产品推荐

