Futter网络请求封装,最骚气、最强大 ,自带loading、错误处理,堪比RxJava效果

时间:2024-03-14 19:54:22

看了网上很多的Flutter网络请求的封装,感觉都不能达到自己想要的那种效果,所以自己动手封装了一个,由于Future很多骚气的操作 未开发出来,所有用了rxDart处理网络请求结果,同样骚气

Flutter架构系列之BaseWidget封装

网络封装实现功能

  • 自动加载loading,统一的请求错误处理,也可以单独处理请求结果
  • 页面销毁,网络请求可以自动取消,也可以不取消
  • 请求结果转化为实体类,方便使用
  • 自定义拦截器

没图说个J… , OK 先上效果图

Futter网络请求封装,最骚气、最强大 ,自带loading、错误处理,堪比RxJava效果

再看调用方式:

    RequestMap.requestLogin().listen((data) {
      List list = data.results;//data已经是解析过后的实体类
    });

是不是很简洁

RequestMap网络统一入口类:

class RequestMap {
  static PublishSubject<LoginResponseEntity> requestLogin<BaseResponse>() {
    String url = "xiandu/category/wow";
    return HttpManager().get<LoginResponseEntity>(url);
  }
}

真正的网络请求HttpManger类

///http请求
class HttpManager {
  static const CONTENT_TYPE_JSON = "application/json";
  static const CONTENT_TYPE_FORM = "application/x-www-form-urlencoded";
  static Dio _dio;
  static final int CONNECR_TIME_OUT = 5000;
  static final int RECIVE_TIME_OUT = 3000;
  static Map<String, CancelToken> _cancelTokens;

  HttpManager._internal() {
    initDio();
  }
  static HttpManager _httpManger = HttpManager._internal();
  factory HttpManager() {
    return _httpManger;
  }

  //get请求
  PublishSubject<T> get<T>(String url,
      {Map<String, dynamic> queryParameters,
      BaseWidgetState baseWidgetState,
      BaseInnerWidgetState baseInnerWidgetState,
      bool isCancelable = true,
      bool isShowLoading = true,
      bool isSHowErrorToast = true}) {
    return _requstHttp<T>(url, true, queryParameters, baseWidgetState,
        baseInnerWidgetState, isCancelable, isShowLoading, isSHowErrorToast);
  }

  //post请求
  PublishSubject<T> post<T>(String url,
      {Map<String, dynamic> queryParameters,
      BaseWidgetState baseWidgetState,
      BaseInnerWidgetState baseInnerWidgetState,
      bool isCancelable = true,
      bool isShowLoading = true,
      bool isSHowErrorToast = true}) {
    return _requstHttp<T>(url, false, queryParameters, baseWidgetState,
        baseInnerWidgetState, isCancelable, isShowLoading, isSHowErrorToast);
  }

  /// 参数说明  url 请求路径
  /// queryParamerers  是 请求参数
  /// baseWidget和baseInnerWidgetState用于 加载loading 和 设置 取消CanselToken
  /// isCancelable 是设置改请求是否 能被取消 , 必须建立在 传入baseWidget或者baseInnerWidgetState的基础之上
  /// isShowLoading 设置是否能显示 加载loading , 同样要建立在传入 baseWidget或者 baseInnerWidgetState 基础之上
  /// isShowErrorToaet  这个是 设置请求失败后 是否要 吐司的
  PublishSubject<T> _requstHttp<T>(String url,
      [bool isGet,
      Map<String, dynamic> queryParameters,
      BaseWidgetState baseWidgetState,
      BaseInnerWidgetState baseInnerWidgetState,
      bool isCancelable,
      bool isShowLoading,
      bool isSHowErrorToast]) {
    Future future;
    PublishSubject<T> publishSubject = PublishSubject<T>();
    CancelToken cancelToken;
    _setLoadingOrcancelAble(baseWidgetState, baseInnerWidgetState, isCancelable,
        isShowLoading, cancelToken);

    if (isGet) {
      future = _dio.get(url,
          queryParameters: queryParameters, cancelToken: cancelToken);
    } else {
      future = _dio.post(url,
          queryParameters: queryParameters, cancelToken: cancelToken);
    }

    future.then((data) {
      //这里要做错误处理
      //需要先过滤 error , 根据和后台的 约定 , 搞清楚什么是失败
      // 什么是成功  , 这里只是根据了干货*的 返回 模拟了一下

      bool isError = json.decode(data.toString())["error"];

      print("---responseData----${data}-----");
      if (isError) {
        callError(publishSubject, MyError(10, "请求失败~"), baseWidgetState,
            baseInnerWidgetState, isShowLoading, isSHowErrorToast);
      } else {
        //这里的解析 请参照 https://www.jianshu.com/p/e909f3f936d6 , dart的字符串 解析蛋碎一地
        publishSubject
            .add(EntityFactory.generateOBJ<T>(json.decode(data.toString())));
        publishSubject.close();

        cancelLoading(baseWidgetState, baseInnerWidgetState, isShowLoading);
      }
    }).catchError((err) {
      callError(publishSubject, MyError(1, err.toString()), baseWidgetState,
          baseInnerWidgetState, isShowLoading, isSHowErrorToast);
    });

    return publishSubject;
  }

  ///请求错误以后 做的一些事情
  void callError(
      PublishSubject publishSubject,
      MyError error,
      BaseWidgetState baseWidgetState,
      BaseInnerWidgetState baseInnerWidgetState,
      bool isShowLoading,
      bool isSHowErrorToast) {
    publishSubject.addError(error);
    publishSubject.close();

    if (isSHowErrorToast) {
      showErrorToast(error.message);
    }

    cancelLoading(baseWidgetState, baseInnerWidgetState, isShowLoading);
  }

  ///取消请求
  static void cancelHttp(String tag) {
    if (_cancelTokens.containsKey(tag)) {
      if (!_cancelTokens[tag].isCancelled) {
        _cancelTokens[tag].cancel();
      }
      _cancelTokens.remove(tag);
    }
  }

  ///配置dio
  void initDio() {
    _dio = Dio();
    // 配置dio实例
//    _dio.options.baseUrl = "http://10.150.20.86/xloan-app-api/";
    _dio.options.baseUrl = "http://gank.io/api/";
    _dio.options.connectTimeout = CONNECR_TIME_OUT; //5s
    _dio.options.receiveTimeout = RECIVE_TIME_OUT;
    _dio.options.contentType = ContentType.parse(CONTENT_TYPE_FORM);
    _cancelTokens = new Map<String, CancelToken>();

//代理设置
//    (dio.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate = (client) {
//      // config the http client
//      client.findProxy = (uri) {
//        //proxy all request to localhost:8888
//        return "PROXY localhost:8888";
//      };
//      // you can also create a new HttpClient to dio
//      // return new HttpClient();
//    };

    //证书配置
//    String PEM="XXXXX"; // certificate content
//    (dio.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate  = (client) {
//      client.badCertificateCallback=(X509Certificate cert, String host, int port){
//        if(cert.pem==PEM){ // Verify the certificate
//          return true;
//        }
//        return false;
//      };
//    };

    /// 添加拦截器
    _dio.interceptors.add(new MyIntercept());
  }

  ///测试是否真的 可以清除 的方法
  static void ListCancelTokens() {
    _cancelTokens.forEach((key, value) {
      print("${key}-----cancel---");
    });
  }

  //配置是否 显示 loading 和 是否能被取消
  void _setLoadingOrcancelAble(
      BaseWidgetState baseWidgetState,
      BaseInnerWidgetState baseInnerWidgetState,
      bool isCancelable,
      bool isShowLoading,
      CancelToken cancelToken) {
    if (baseWidgetState != null) {
      if (isShowLoading) {
        baseWidgetState.setLoadingWidgetVisible(true);
      }
      //为了 能够取消 请求
      if (isCancelable) {
        cancelToken = _cancelTokens[baseWidgetState.getClassName()] == null
            ? CancelToken()
            : _cancelTokens[baseWidgetState.getClassName()];
        _cancelTokens[baseWidgetState.getClassName()] = cancelToken;
      }
    }

    //这里主要是做 是否 加载loading和是否 页面销毁的时候
    if (baseInnerWidgetState != null) {
      if (isShowLoading) {
        baseInnerWidgetState.setLoadingWidgetVisible(true);
      }
      //为了能够取消请求
      if (isCancelable) {
        cancelToken = _cancelTokens[baseInnerWidgetState.getClassName()] == null
            ? CancelToken()
            : _cancelTokens[baseInnerWidgetState.getClassName()];
        _cancelTokens[baseInnerWidgetState.getClassName()] = cancelToken;
      }
    }
  }

  void showErrorToast(String message) {
    //TODO 统一错误提示
  }

  void cancelLoading(BaseWidgetState baseWidgetState,
      BaseInnerWidgetState baseInnerWidgetState, bool isShowLoading) {
    if (baseWidgetState != null) {
      if (isShowLoading) {
        baseWidgetState.setLoadingWidgetVisible(false);
      }
    }
    if (baseInnerWidgetState != null) {
      if (isShowLoading) {
        baseInnerWidgetState.setLoadingWidgetVisible(false);
      }
    }
  }
}

class MyError {
  int code;
  String message;
  MyError(this.code, this.message);
}

没有很难得逻辑,就是简单的处理,希望能帮到你~
结合BaseWidget可以达到很完美的效果,代码已经上传github,欢迎查看

Flutter架构系列之BaseWidget封装

目标是致力于打造一套完整的 flutter开发架构,不用高大上的工具,不用复杂了上手,追求简单、好用