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
相关产品推荐
相关产品推荐

