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

如何在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.17 17:01:34