如何在无Context的情况下使用Provider调用token刷新方法
问题
我想在接口返回401状态码时,通过Provider调用接口刷新token,但Provider调用函数需要Context。现在在拦截器里拿不到Context,有没有不用Context就能调用该函数的方法?
拦截器代码如下:
class ExpiredTokenRetryPolicy extends RetryPolicy { @override Future<bool> shouldAttemptRetryOnResponse(BaseResponse response) async { if (response.statusCode == 401) { // 执行token刷新,获取新token并更新到安全存储 Provider.of<Auth>(context, listen: false).restoreAccessToken(); } return false; } }
我用Provider存储接口返回的变量,所以没法直接调用函数,restoreAccessToken函数代码如下:
Future<void> restoreAccessToken() async { final url = '${Ninecabsapi().urlHost}${Ninecabsapi().login}/$sessionId'; var response = await http.patch( Uri.parse(url), headers: { 'Content-Type': 'application/json; charset=UTF-8', 'Authorization': token! }, body: json.encode( {"refresh_token": refreshtoken}, ), ); var userDetails = json.decode(response.body); if (response.statusCode == 401) { print(userDetails['messages']); } print(userDetails); sessionId = userDetails['data']['session_id']; accessToken = userDetails['data']['access_token']; accessTokenExpiryDate = DateTime.now().add( Duration(seconds: userDetails['data']['access_token_expiry']), ); refreshToken = userDetails['data']['refresh_token']; refreshTokenExpiryDate = DateTime.now().add( Duration(seconds: userDetails['data']['refresh_token_expiry']), ); print(userDetails); notifyListeners(); final prefs = await SharedPreferences.getInstance(); final userData = json.encode({ 'sessionId': sessionId, 'refreshToken': refreshToken, 'refreshExpiry': refreshTokenExpiryDate!.toIso8601String(), 'accessToken': accessToken, 'accessTokenExpiry': accessTokenExpiryDate!.toIso8601String() }); prefs.setString('userData', userData); reset(); }
解决方案
1. 全局持有Auth实例
在App初始化时创建Auth实例并保存为全局变量,拦截器里直接调用该实例的方法:
// 全局变量 Auth? globalAuthInstance; // 在main函数或App启动时初始化 void main() { globalAuthInstance = Auth(); runApp( ChangeNotifierProvider.value( value: globalAuthInstance!, child: MyApp(), ), ); } // 拦截器里调用 class ExpiredTokenRetryPolicy extends RetryPolicy { @override Future<bool> shouldAttemptRetryOnResponse(BaseResponse response) async { if (response.statusCode == 401) { await globalAuthInstance?.restoreAccessToken(); } return false; } }
注意要保证全局实例和Provider中使用的是同一个,避免状态不一致。
2. 使用ProviderContainer(Provider 5.0+支持)
如果用的是新版本Provider,可以通过ProviderContainer获取实例,无需Context:
// 初始化时保存ProviderContainer late final ProviderContainer container; void main() { container = ProviderContainer(); runApp( UncontrolledProviderScope( container: container, child: MyApp(), ), ); } // 拦截器里调用 class ExpiredTokenRetryPolicy extends RetryPolicy { @override Future<bool> shouldAttemptRetryOnResponse(BaseResponse response) async { if (response.statusCode == 401) { final auth = container.read(Auth.provider); await auth.restoreAccessToken(); } return false; } }
需要先在Auth类中定义对应的provider:
class Auth extends ChangeNotifier { static final provider = ChangeNotifierProvider<Auth>((ref) => Auth()); // ... 其他代码 }
3. 重构刷新逻辑为独立服务类
把token刷新的核心逻辑从Auth类抽离成独立服务,解耦状态管理和业务逻辑:
// 独立的token刷新服务 class TokenRefreshService { static Future<Map<String, dynamic>> refreshToken(String currentToken, String refreshToken, String sessionId) async { final url = '${Ninecabsapi().urlHost}${Ninecabsapi().login}/$sessionId'; var response = await http.patch( Uri.parse(url), headers: { 'Content-Type': 'application/json; charset=UTF-8', 'Authorization': currentToken }, body: json.encode( {"refresh_token": refreshToken}, ), ); return json.decode(response.body); } } // 修改Auth类的restoreAccessToken方法 Future<void> restoreAccessToken() async { final userDetails = await TokenRefreshService.refreshToken(token!, refreshtoken, sessionId); if (response.statusCode == 401) { print(userDetails['messages']); return; } // 原有更新状态和存储的逻辑不变 sessionId = userDetails['data']['session_id']; accessToken = userDetails['data']['access_token']; accessTokenExpiryDate = DateTime.now().add( Duration(seconds: userDetails['data']['access_token_expiry']), ); // ... 其他状态更新代码 notifyListeners(); // ... 保存到SharedPreferences代码 } // 拦截器里结合全局Auth实例调用 class ExpiredTokenRetryPolicy extends RetryPolicy { @override Future<bool> shouldAttemptRetryOnResponse(BaseResponse response) async { if (response.statusCode == 401) { await globalAuthInstance?.restoreAccessToken(); } return false; } }
这种方式让职责更清晰,也方便后续单独维护刷新逻辑。
内容的提问来源于stack exchange,提问作者Santosh Kumar
相关产品推荐
相关产品推荐

