Flutter中GoRouter导航失效问题求助及原因排查
GoRouter + ShellRoute 导航失效问题排查与修复
问题现象
使用GoRouter和ShellRoute开发时,仅认证失败跳转Login页面的功能正常,其余所有导航操作完全失效。已将routerConfig配置到MaterialApp中,应用全程无报错,初步怀疑与ShellRoute的key设置方式有关。
-- 更新 --
进一步排查发现:每次调用go或push跳转新路由时,GoRouter会整体重建,导致redirect逻辑被重复触发。由于GoRouter没有内置完善的Guard机制,问题根源在于redirect逻辑处理不当。
当前代码配置
MaterialApp配置
MaterialApp.router( debugShowCheckedModeBanner: false, title: 'iHub', theme: theme.light(settings.value.sourceColor), darkTheme: theme.dark(settings.value.sourceColor), themeMode: theme.themeMode(), routerDelegate: appRouter.routerDelegate, routeInformationProvider: appRouter.routeInformationProvider, routeInformationParser: appRouter.routeInformationParser, );
GoRouter配置
final _parentKey = GlobalKey<NavigatorState>(); final _shellKey = GlobalKey<NavigatorState>(); final appRouter = GoRouter( initialLocation: Routes.login, redirect: (BuildContext context, GoRouterState state) { final bool isAuthenticated = context.read<AuthenticationBloc>().isAuthenticated; if (!isAuthenticated) { return Routes.login; } else { return Routes.dashboard; } }, navigatorKey: _parentKey, routes: [ ShellRoute( // 带导航栏的Shell路由 navigatorKey: _shellKey, builder: (context, state, child) { return RootLayout(child: child); }, routes: [ GoRoute( name: 'dashboard', path: Routes.dashboard, pageBuilder: (context, state) => NoTransitionPage( key: state.pageKey, child: const DashboardPage(), ), ), GoRoute( name: 'documents', path: Routes.documentsList, pageBuilder: (context, state) => NoTransitionPage( key: state.pageKey, child: const DocumentsPage(), ), ), GoRoute( name: 'websites', path: Routes.documentsWebsitesList, pageBuilder: (context, state) => NoTransitionPage( key: state.pageKey, child: const WebsitesPage(), ), ), GoRoute( name: 'tasks', path: Routes.tasksList, pageBuilder: (context, state) => NoTransitionPage( key: state.pageKey, child: const ListTasksPage(), ), ), ], ), // 认证相关路由 GoRoute( name: 'login', parentNavigatorKey: _parentKey, path: Routes.login, pageBuilder: (context, state) => const NoTransitionPage( key: ValueKey('login'), child: LoginPage(), ), ), GoRoute( name: 'register', parentNavigatorKey: _parentKey, path: Routes.register, pageBuilder: (context, state) => const NoTransitionPage( key: ValueKey('register'), child: RegisterPage(), ), ), // 文档相关路由 GoRoute( name: 'addDocument', parentNavigatorKey: _parentKey, path: Routes.addDocument, pageBuilder: (context, state) => NoTransitionPage( key: state.pageKey, child: const AddDocumentPage(), ), ), // 网站相关路由 GoRoute( name: 'addWebsite', parentNavigatorKey: _parentKey, path: Routes.addWebsite, pageBuilder: (context, state) => NoTransitionPage( key: state.pageKey, child: const AddWebsitePage(), ), ), // 任务相关路由 GoRoute( name: 'addTask', parentNavigatorKey: _parentKey, path: Routes.addTask, pageBuilder: (context, state) => NoTransitionPage( key: state.pageKey, child: const AddTaskPage(), ), ), // 搜索路由 GoRoute( name: 'search', parentNavigatorKey: _parentKey, path: Routes.search, pageBuilder: (context, state) => NoTransitionPage( key: state.pageKey, child: const SearchPage(), ), ), ], );
问题修复
你的核心问题是redirect逻辑写死了跳转路径:无论当前处于什么认证后页面,只要isAuthenticated为true,就强制跳转到Routes.dashboard,导致其他导航操作被直接覆盖。
修改redirect逻辑,仅在特定场景下触发跳转,其余情况返回null放行:
redirect: (BuildContext context, GoRouterState state) { final bool isAuthenticated = context.read<AuthenticationBloc>().isAuthenticated; // 未认证状态,且当前不在登录/注册页面,跳转登录 if (!isAuthenticated) { if (state.location != Routes.login && state.location != Routes.register) { return Routes.login; } } else { // 已认证状态,且当前在登录/注册页面,跳转首页 if (state.location == Routes.login || state.location == Routes.register) { return Routes.dashboard; } } // 其他情况不跳转,返回null让GoRouter处理正常导航 return null; },
额外优化建议:
确保AuthenticationBloc的状态变化能触发GoRouter重新执行redirect逻辑,可以通过refreshListenable参数传入Bloc的状态监听:
// 先实现一个监听Bloc流的Listenable类 class GoRouterRefreshStream extends ChangeNotifier { GoRouterRefreshStream(Stream<dynamic> stream) { _subscription = stream.listen((_) => notifyListeners()); } late final StreamSubscription<dynamic> _subscription; @override void dispose() { _subscription.cancel(); super.dispose(); } } // 在GoRouter配置中添加 final appRouter = GoRouter( refreshListenable: GoRouterRefreshStream(context.read<AuthenticationBloc>().stream), // ... 其他原有配置 );
内容的提问来源于stack exchange,提问作者Adrian0012
相关产品推荐
相关产品推荐

