如何在Dart拦截器中使用Provider?无BuildContext的解决方案
解决Dio拦截器中使用Provider控制加载状态的方案
方案1:直接传递LoadingState实例给拦截器
不需要依赖BuildContext获取Provider实例,初始化拦截器时直接把LoadingState实例传入即可:
class LoggingInterceptor extends Interceptor { final LoadingState loadingState; LoggingInterceptor(this.loadingState); @override void onRequest(RequestOptions options, RequestInterceptorHandler handler) async { loadingState.startLoader(); handler.next(options); } @override void onResponse(Response response, ResponseInterceptorHandler handler) async { loadingState.stopLoader(); handler.next(response); } // 必须处理请求失败场景,避免加载状态一直挂起 @override void onError(DioException err, ErrorInterceptorHandler handler) async { loadingState.stopLoader(); handler.next(err); } }
在有BuildContext的地方初始化拦截器并传入实例:
// 示例:在页面initState或全局网络初始化代码中 final loadingState = Provider.of<LoadingState>(context, listen: false); Dio dio = Dio(); dio.interceptors.add(LoggingInterceptor(loadingState));
方案2:使用全局单例的LoadingState
把LoadingState改成单例模式,拦截器里直接调用单例方法,完全脱离BuildContext依赖:
class LoadingState extends ChangeNotifier { // 单例实例 static final LoadingState _instance = LoadingState._internal(); factory LoadingState() => _instance; LoadingState._internal(); bool _isLoading = false; bool get isLoading => _isLoading; void startLoader() { _isLoading = true; notifyListeners(); } void stopLoader() { _isLoading = false; notifyListeners(); } }
拦截器中直接调用单例方法:
class LoggingInterceptor extends Interceptor { @override void onRequest(RequestOptions options, RequestInterceptorHandler handler) async { LoadingState().startLoader(); handler.next(options); } @override void onResponse(Response response, ResponseInterceptorHandler handler) async { LoadingState().stopLoader(); handler.next(response); } @override void onError(DioException err, ErrorInterceptorHandler handler) async { LoadingState().stopLoader(); handler.next(err); } }
注意:Provider初始化要对应改成单例模式:
ChangeNotifierProvider.value( value: LoadingState(), child: YourAppWidget(), )
方案3:通过Dio的extra参数传递BuildContext(不推荐)
发起请求时将context存入options.extra,拦截器中取出使用,但需注意判断context是否已销毁,避免内存泄漏:
// 发起请求时传入context dio.get('/api/data', options: Options(extra: {'context': context}));
拦截器中使用:
class LoggingInterceptor extends Interceptor { @override void onRequest(RequestOptions options, RequestInterceptorHandler handler) async { final context = options.extra['context'] as BuildContext?; if (context != null && context.mounted) { Provider.of<LoadingState>(context, listen: false).startLoader(); } handler.next(options); } @override void onResponse(Response response, ResponseInterceptorHandler handler) async { final context = response.requestOptions.extra['context'] as BuildContext?; if (context != null && context.mounted) { Provider.of<LoadingState>(context, listen: false).stopLoader(); } handler.next(response); } @override void onError(DioException err, ErrorInterceptorHandler handler) async { final context = err.requestOptions.extra['context'] as BuildContext?; if (context != null && context.mounted) { Provider.of<LoadingState>(context, listen: false).stopLoader(); } handler.next(err); } }
此方案需每次请求手动传context,且存在context生命周期风险,优先推荐前两种方案。
内容的提问来源于stack exchange,提问作者Altaf Shaikh
相关产品推荐
相关产品推荐

