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

Flutter中基于BLoC Provider实现登出并跳转登录页的问题

BLoC Provider环境下双Token过期后无法跳转登录页的解决方法

场景概述

  • 首次在大型项目中使用BLoC Provider做状态管理,包含登录页和登录后的Reservations预订列表页
  • 采用Retrofit调用API,OAuth密码授权模式,登录成功返回access/refresh token
  • 已实现拦截器逻辑:access token过期时自动调用刷新接口获取新token;当access和refresh token均过期返回401时,需要登出用户并跳转回/login页

问题现象

双Token均过期触发401后,已完成安全存储中用户信息的移除,但页面停留在Reservations页的加载状态,无法自动跳转至登录页

解决方案步骤

1. 拦截器中标记登出标识

修改Retrofit拦截器的onError方法,当检测到双Token过期的401错误时,添加自定义标识,方便后续Bloc识别该错误类型:

// 拦截器onError示例
@override
Future<dynamic> onError(DioException err) async {
  if (err.response?.statusCode == 401) {
    // 判断是否为双Token都过期的情况(比如刷新token请求也返回401)
    bool isDoubleTokenExpired = /* 你的业务判断逻辑 */;
    if (isDoubleTokenExpired) {
      // 给错误添加自定义标识
      err = DioException(
        requestOptions: err.requestOptions,
        response: err.response,
        error: 'DOUBLE_TOKEN_EXPIRED',
      );
    }
  }
  return super.onError(err);
}

2. 预订页Bloc处理登出事件

在Reservations相关Bloc中,捕获带DOUBLE_TOKEN_EXPIRED标识的错误时,触发登出事件并清理本地存储:

// ReservationsBloc示例
class ReservationsBloc extends Bloc<ReservationsEvent, ReservationsState> {
  // ... 其他初始化代码
  @override
  Stream<ReservationsState> mapEventToState(ReservationsEvent event) async* {
    if (event is FetchReservations) {
      try {
        final reservations = await repository.getReservations();
        yield ReservationsLoaded(reservations);
      } on DioException catch (e) {
        if (e.error == 'DOUBLE_TOKEN_EXPIRED') {
          add(LogoutRequested());
        } else {
          yield ReservationsError(e.message ?? '请求失败');
        }
      }
    } else if (event is LogoutRequested) {
      // 清理安全存储中的用户凭证
      await secureStorage.deleteAll();
      // 发送登出状态
      yield ReservationsLogoutState();
    }
  }
}

3. Main.dart中监听状态并完成跳转

在main.dart的顶层BlocProvider中,监听ReservationsBloc的状态,捕获到登出状态时重置相关Bloc并跳转登录页:

// main.dart示例
void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MultiBlocProvider(
      providers: [
        BlocProvider(create: (context) => ReservationsBloc(repository: ReservationsRepository())),
        BlocProvider(create: (context) => AuthBloc(repository: AuthRepository())),
      ],
      child: MaterialApp(
        routes: {
          '/login': (context) => const LoginPage(),
          '/reservations': (context) => const ReservationsPage(),
        },
        home: BlocListener<ReservationsBloc, ReservationsState>(
          listener: (context, state) {
            if (state is ReservationsLogoutState) {
              // 重置AuthBloc状态
              context.read<AuthBloc>().add(AuthReset());
              // 清空栈并跳转登录页
              Navigator.pushNamedAndRemoveUntil(context, '/login', (route) => false);
            }
          },
          child: // 初始页面判断(比如根据本地是否有token决定跳转登录页还是预订页)
        ),
      ),
    );
  }
}

核心逻辑说明

通过拦截器标记特定错误,让Bloc精准识别双Token过期场景,再通过顶层BlocListener监听登出状态,完成状态重置和页面栈清理跳转,解决了之前仅清理存储但未触发状态更新导致页面卡住的问题。

内容的提问来源于stack exchange,提问作者user13289818

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 00:38:11