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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 19:42:03