注意flutter 的 rxdart 请求超时回调不在onError里面 需要在请求地方添加timeout,超时回调会在此处,如下图:

RequestMap.requestNotPayOrder(context)
        .timeout(Duration(seconds: Utils.getConnectTime()), onTimeout: (sink) {
      ShowLoadLoadingDialog.hideLoadingDialog();
    }).listen((data) async {
      ShowLoadLoadingDialog.hideLoadingDialog();
      if (data?.data != null) {
        if (data.data.appNoPwd) {
          if (data.data.notPayNum == 0) {
            String result =
                await scanQrCodePlatform.invokeMethod('startCaptureActivity');
            handleCaptureResult(result);
          } else {
            showDialogType = 1;
            showDialog<Null>(
                context: context, //BuildContext对象
                barrierDismissible: true,
                builder: (BuildContext context) {
                  return getCustomDialog();
                });
          }
        } else {
          ToastUtils.toastUtils(context, msg: S.of(context).mian_page_no_pwd);
        }
      }
    }, onError: (err) {
      ShowLoadLoadingDialog.hideLoadingDialog();
    });

以上是原先的解决方案,不解耦,每个请求都需要写一个请求超时的回调,很不方便,所以在请求dio返回的future中做监听操作,如下所示,避免了在每个请求里面监听超时的操作:

/// 参数说明  url 请求路径
  /// queryParamerers  是 请求参数
  /// baseWidget和baseInnerWidgetState用于 加载loading 和 设置 取消CanselToken
  /// isCancelable 是设置改请求是否 能被取消 , 必须建立在 传入baseWidget或者baseInnerWidgetState的基础之上
  /// isShowLoading 设置是否能显示 加载loading , 同样要建立在传入 baseWidget或者 baseInnerWidgetState 基础之上
  /// isShowErrorToaet  这个是 设置请求失败后 是否要 吐司的
  PublishSubject<T> _requstHttp<T>(String url,
      [bool isGet,
      queryParameters,
      BaseIntercept baseIntercept,
      bool isCancelable,
      bool isShowToast = true]) {
    Future future;
    PublishSubject<T> publishSubject = PublishSubject<T>();
    CancelToken cancelToken;
    _setInterceptOrcancelAble(baseIntercept, isCancelable, cancelToken);
    Map<String, String> headers = Map<String, String>();
    if (Utils.isString(SpUtil.getString(SharedPreferencesTag.SKEY))) {
      headers['UCODE'] = SpUtil.getString(SharedPreferencesTag.SKEY);
      _dio.options.headers = headers;
    }
//    _dio.options.queryParameters =
//        queryParameters; //注意自定义baseOption会让下面的queryParameters失效,所以此处需要赋值给他
    this.isShowToast = isShowToast;
    if (isGet) {
      _dio.options.connectTimeout = 2000; //2s
      _dio.options.receiveTimeout = 1000;
    } else {
      _dio.options.connectTimeout = CONNECR_TIME_OUT; //5s
      _dio.options.receiveTimeout = RECIVE_TIME_OUT;
    }
    if (isGet) {
      future = _dio.get(url,
          cancelToken: cancelToken, queryParameters: queryParameters);
    } else {
      future = _dio.post(url,
          cancelToken: cancelToken,
          queryParameters: queryParameters,
          data: queryParameters);
    }
    LogUtil.e("api 请求:  $url \nisGet$isGet params$queryParameters header" +
        headers.toString());
    future.then((data) {
      //这里要做错误处理
      //需要先过滤 error , 根据和后台的 约定 , 搞清楚什么是失败
      // 什么是成功  , 这里只是根据了干货集中营的 返回 模拟了一下
      LogUtil.e("data$data");
      String code = json.decode(data.toString())["code"];
      String msg = json.decode(data.toString())["msg"];
      msg = msg == null || msg == "" ? S.of(mContext).request_fail : msg;

      LogUtil.e("api ---responseData----\n"
          "${data}"
          "\n-----");
      if (code != "0") {
        callError(
          publishSubject,
          MyError(int.parse(code), msg),
          baseIntercept,
        );
      } else {
        //这里的解析 请参照 https://www.jianshu.com/p/e909f3f936d6 , dart的字符串 解析蛋碎一地
        publishSubject
            .add(EntityFactory.generateOBJ<T>(json.decode(data.toString())));
        publishSubject.close();
        cancelLoading(baseIntercept);
      }
    }, onError: (err) {
      if (err is DioError) {
        switch (err.type) {
          case DioErrorType.CONNECT_TIMEOUT:
            callError(
                publishSubject,
                MyError(NetTypeConfig.CONNECT_TIMEOUT,
                    S.of(mContext).net_connect_timeout),
                baseIntercept);
            break;
          case DioErrorType.SEND_TIMEOUT:
            callError(
                publishSubject,
                MyError(NetTypeConfig.SEND_TIMEOUT,
                    S.of(mContext).net_send_timeout),
                baseIntercept);
            break;
          case DioErrorType.RECEIVE_TIMEOUT:
            callError(
                publishSubject,
                MyError(NetTypeConfig.RECEIVE_TIMEOUT,
                    S.of(mContext).net_receive_timeout),
                baseIntercept);
            break;
          case DioErrorType.RESPONSE:
            callError(
                publishSubject,
                MyError(NetTypeConfig.RESPONSE, S.of(mContext).net_response),
                baseIntercept);
            break;
          case DioErrorType.CANCEL:
            callError(
                publishSubject,
                MyError(NetTypeConfig.CANCEL, S.of(mContext).net_cancel),
                baseIntercept);
            break;
          case DioErrorType.DEFAULT:
            callError(
                publishSubject,
                MyError(NetTypeConfig.DEFAULT, S.of(mContext).net_defult),
                baseIntercept);
            break;
        }
      }
    }).catchError((err) {
      LogUtil.e("catchError${err.toString()}");
      callError(publishSubject, MyError(-1000, S.of(mContext).request_error),
          baseIntercept);
    });

    return publishSubject;
  }