Flutter中Go Router+BLoC路由鉴权的页面刷新异常问题
问题解决:Flutter Web中GoRouter结合BLoC的会话验证刷新问题
问题背景
在Flutter Web应用中使用GoRouter+BLoC管理用户会话,进入特定路由时需要验证用户是否已认证。BLoC内置token验证方法,但页面刷新时路由逻辑先执行,BLoC的token验证逻辑滞后,导致路由守卫读取到isAuth的初始值false(实际token存在,后续isAuth会更新为true),引发错误跳转。
解决方案
方案1:异步路由守卫,等待BLoC完成验证
GoRouter的redirect支持异步函数,可在其中等待SessionBloc完成token验证,再根据最终isAuth状态决定跳转逻辑。
修改后的Router代码:
final GlobalKey<NavigatorState> _navigatorKey = GlobalKey<NavigatorState>(); class AppRouter { final GoRouter router = GoRouter( initialLocation: Uri.base.toString().replaceFirst(Uri.base.origin, ''), navigatorKey: _navigatorKey, routes: <RouteBase>[ GoRoute( path: Routes.home, builder: (BuildContext context, GoRouterState state) => BlocProvider<SignInCubit>.value( value: locator<SignInCubit>(), child: const SignInScreen(), ), ), GoRoute( path: Routes.paymentConfirmation, builder: (BuildContext context, GoRouterState state) => BlocProvider<PaymentConfirmationCubit>( create: (_) => locator<PaymentConfirmationCubit>(), child: const PaymentConfirmation(), ), redirect: (BuildContext context, _) async { // 等待SessionBloc完成加载状态 final sessionState = await context.read<SessionBloc>().stream .firstWhere((state) => !state.isLoading); return sessionState.isAuth ? null : Routes.home; }, ), // 其余路由保持不变 GoRoute( path: Routes.otpCodeValidation, builder: (BuildContext context, GoRouterState state) => BlocProvider<OtpCodeCubit>.value( value: locator<OtpCodeCubit>(), child: const OtpCodeValidation(), ), ), GoRoute( path: Routes.paymentValidation, builder: (BuildContext context, GoRouterState state) => const PaymentValidation(), ), GoRoute( path: Routes.transactionsList, builder: (BuildContext context, GoRouterState state) => const TransactionsList(), ), GoRoute( path: Routes.transactionDetail, builder: (BuildContext context, GoRouterState state) => const TransactionDetail(), ), ], ); }
方案2:提前完成Session验证再启动应用
在main函数中先初始化SessionBloc并触发验证,等待验证完成后再启动App,确保路由初始化时isAuth已是正确状态。
修改main函数:
void main() async { WidgetsFlutterBinding.ensureInitialized(); // 初始化依赖注入 await setupLocator(); // 初始化SessionBloc并等待验证完成 final sessionBloc = locator<SessionBloc>(); sessionBloc.add(const SessionStarted()); await sessionBloc.stream.firstWhere((state) => !state.isLoading); runApp(const MyApp()); }
同时调整SessionBloc的初始状态,明确加载状态:
class SessionState extends Equatable { final User user; final bool isAuth; final bool isLoading; const SessionState({ required this.user, this.isAuth = false, this.isLoading = false, }); SessionState copyWith({ User? user, bool? isAuth, bool? isLoading, }) { return SessionState( user: user ?? this.user, isAuth: isAuth ?? this.isAuth, isLoading: isLoading ?? this.isLoading, ); } @override List<Object> get props => [user, isAuth, isLoading]; } // 更新SessionBloc初始状态 class SessionBloc extends Bloc<SessionEvent, SessionState> { SessionBloc({ required this.validateTokenUseCase, required this.signInUseCase, }) : super(SessionState(user: User.empty(), isLoading: false)) { on<SessionStarted>(_onSessionStarted); } // 其余代码保持不变 }
注意事项
- 方案1无需启动页,路由会在验证完成后跳转,可配合全局加载指示器优化用户体验
- 方案2会在App启动阶段等待验证完成,适合需要展示启动页的场景,避免路由跳转闪烁
内容的提问来源于stack exchange,提问作者RoachCat
相关产品推荐
相关产品推荐

