基于GoRouter实现随登录状态动态切换的嵌套路由底部导航
动态切换认证状态下的StatefulShellRoute分支方案
针对你的需求,这里提供一个兼顾深度链接支持和避免不必要请求的实现方案,核心是单GoRouter配置+动态生成StatefulShellBranch+路由守卫控制访问:
核心思路
用同一个GoRouter实例,通过监听认证状态动态生成对应状态的导航分支,同时用redirect逻辑确保未登录用户无法访问私有页面;把私有页面的初始化(比如网络请求)放在页面内部的initState中,而非路由配置阶段,这样未登录时这些页面不会被加载,自然不会触发多余请求。
具体实现
1. 抽离不同状态的导航分支配置
先把登录/未登录的分支单独定义,方便动态切换:
// 未登录时的公共导航分支 final List<StatefulShellBranch> _publicBranches = [ StatefulShellBranch( routes: [ GoRoute( path: '/home', builder: (context, state) => const PublicHomePage(), ), ], ), StatefulShellBranch( routes: [ GoRoute( path: '/login', builder: (context, state) => const LoginPage(), ), ], ), ]; // 登录后的私有导航分支 final List<StatefulShellBranch> _privateBranches = [ StatefulShellBranch( routes: [ GoRoute( path: '/dashboard', builder: (context, state) => const DashboardPage(), // 嵌套子路由示例 routes: [ GoRoute( path: 'details/:id', builder: (context, state) => DetailsPage(id: state.pathParameters['id']!), ), ], ), ], ), StatefulShellBranch( routes: [ GoRoute( path: '/profile', builder: (context, state) => const ProfilePage(), ), ], ), ];
2. 监听认证状态动态生成路由
用ChangeNotifier管理认证状态,让GoRouter通过refreshListenable监听状态变化,自动更新分支和底部导航:
// 管理认证状态的类 class AuthState extends ChangeNotifier { bool _isLoggedIn = false; bool get isLoggedIn => _isLoggedIn; void login() { _isLoggedIn = true; notifyListeners(); // 通知GoRouter刷新配置 } void logout() { _isLoggedIn = false; notifyListeners(); } } // 全局实例化认证状态 final authState = AuthState(); // 配置GoRouter final GoRouter router = GoRouter( refreshListenable: authState, // 重定向逻辑:处理未登录访问私有页面、已登录访问登录页的情况 redirect: (context, state) { final isLoggedIn = authState.isLoggedIn; final targetPath = state.subloc; // 未登录时访问私有路由,跳转到登录页 if (!isLoggedIn && (targetPath.startsWith('/dashboard') || targetPath.startsWith('/profile'))) { return '/login'; } // 已登录时访问登录页,跳转到仪表盘 if (isLoggedIn && targetPath == '/login') { return '/dashboard'; } return null; // 无需重定向 }, routes: [ StatefulShellRoute.indexedStack( builder: (context, state, navigationShell) { // 动态渲染底部导航栏 return Scaffold( body: navigationShell, bottomNavigationBar: BottomNavigationBar( currentIndex: navigationShell.currentIndex, onTap: (index) { navigationShell.goBranch( index, initialLocation: index == navigationShell.currentIndex, ); }, items: authState.isLoggedIn ? const [ BottomNavigationBarItem(icon: Icon(Icons.dashboard), label: '仪表盘'), BottomNavigationBarItem(icon: Icon(Icons.person), label: '我的'), ] : const [ BottomNavigationBarItem(icon: Icon(Icons.home), label: '首页'), BottomNavigationBarItem(icon: Icon(Icons.login), label: '登录'), ], ), ); }, // 根据认证状态动态选择分支 branches: authState.isLoggedIn ? _privateBranches : _publicBranches, ), // 其他公共路由(比如重置密码) GoRoute( path: '/reset-password', builder: (context, state) => const ResetPasswordPage(), ), ], );
3. 页面内部延迟初始化请求
把私有页面的网络请求等初始化逻辑放在initState中,只有当用户实际进入页面(且已通过路由守卫验证)时才执行:
class DashboardPage extends StatefulWidget { const DashboardPage({super.key}); @override State<DashboardPage> createState() => _DashboardPageState(); } class _DashboardPageState extends State<DashboardPage> { @override void initState() { super.initState(); // 仅当页面加载时才发起请求 _loadDashboardData(); } Future<void> _loadDashboardData() async { // 这里写获取仪表盘数据的逻辑 } @override Widget build(BuildContext context) { return const Center(child: Text('仪表盘')); } }
方案优势
- 完美支持深度链接:所有路由在同一GoRouter配置中,重定向逻辑可以直接处理深度链接的跳转
- 避免不必要请求:私有页面的初始化逻辑在页面内部,未登录时这些页面不会被实例化,自然不会触发网络请求
- 动态切换无感知:认证状态变化时,GoRouter自动刷新分支和底部导航,无需手动切换整个路由配置
- 状态安全:通过redirect逻辑确保未登录用户无法访问私有页面,即使直接输入URL也会被重定向
内容的提问来源于stack exchange,提问作者DevelJoe
相关产品推荐
相关产品推荐

