如何在RetryPolicy拦截器中传递BuildContext处理401重定向
解决http_interceptor中无法传递BuildContext的问题
方案1:使用全局导航键(GlobalKey)
这是最常用的解决方案,无需在拦截器中传递BuildContext,通过全局键直接控制页面导航。
步骤实现
- 在全局范围定义导航键
final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
- 在
MaterialApp中配置该导航键
MaterialApp( navigatorKey: navigatorKey, // 其他页面路由、主题等配置... )
- 修改
refreshToken方法,移除BuildContext参数,改用全局导航键处理跳转
class AuthedApiClient { http.Client client = InterceptedClient.build( interceptors: [AuthorizationInterceptor()], retryPolicy: ExpiredTokenRetryPolicy(), requestTimeout: const Duration(seconds: 10), ); static Future<void> refreshToken() async { LoginService loginService = LoginService(); try { await loginService.refreshLogin(); // 必须加await,确保令牌刷新完成后再重试请求 } catch (e) { if (e is RefreshTokenException) { Fluttertoast.showToast( msg: e.message, toastLength: Toast.LENGTH_SHORT, gravity: ToastGravity.BOTTOM, timeInSecForIosWeb: 1, textColor: Colors.black, backgroundColor: ThemeColors.errorToastMessage, fontSize: 12.0, ); } // 通过全局导航键跳转登录页 navigatorKey.currentState?.pushReplacementNamed('/'); } } }
- 重试策略中直接调用无参数的
refreshToken
class ExpiredTokenRetryPolicy extends RetryPolicy { @override Future<bool> shouldAttemptRetryOnResponse(ResponseData response) async { if (response.statusCode == 401) { await AuthedApiClient.refreshToken(); return true; } return false; } }
方案2:将导航键注入到重试策略中
如果不想依赖全局变量,可以在创建重试策略时传入导航键,保持代码封装性。
步骤实现
- 修改重试策略类,添加导航键属性
class ExpiredTokenRetryPolicy extends RetryPolicy { final GlobalKey<NavigatorState> navigatorKey; ExpiredTokenRetryPolicy({required this.navigatorKey}); @override Future<bool> shouldAttemptRetryOnResponse(ResponseData response) async { if (response.statusCode == 401) { await AuthedApiClient.refreshToken(navigatorKey); return true; } return false; } }
- 调整
refreshToken方法,接收导航键参数
class AuthedApiClient { static Future<void> refreshToken(GlobalKey<NavigatorState> navigatorKey) async { LoginService loginService = LoginService(); try { await loginService.refreshLogin(); } catch (e) { if (e is RefreshTokenException) { Fluttertoast.showToast( msg: e.message, toastLength: Toast.LENGTH_SHORT, gravity: ToastGravity.BOTTOM, timeInSecForIosWeb: 1, textColor: Colors.black, backgroundColor: ThemeColors.errorToastMessage, fontSize: 12.0, ); } navigatorKey.currentState?.pushReplacementNamed('/'); } } // 创建Client时传入带导航键的重试策略 http.Client client = InterceptedClient.build( interceptors: [AuthorizationInterceptor()], retryPolicy: ExpiredTokenRetryPolicy(navigatorKey: navigatorKey), requestTimeout: const Duration(seconds: 10), ); }
关键注意事项
- 原代码中
loginService.refreshLogin()未加await,会导致未完成令牌刷新就重试请求,必须添加await确保令牌更新后再执行重试逻辑。 - 使用全局导航键时,需确保
MaterialApp已完成初始化,否则currentState可能为null。
内容的提问来源于stack exchange,提问作者rafaelpadu
相关产品推荐
相关产品推荐

