网络恳求结构挑选用 Dio传送门,截止当前最新的版本是 dio:^4.0.4

本文仅是对 dio 结构进行必定的封装

1、引入

在 pubspec.yaml 文件中增加

dependencies:
  # The following adds the Cupertino Icons font to your application.
  # Use with the CupertinoIcons class for iOS style icons.
  cupertino_icons: ^1.0.2
  dio: ^4.0.4

2、装备

a、Android 网络权限装备及 Https 正式的校验

打开 app 目录下的 AndroidManifest.xml 文件,并在其间 application 增加 “networkSecurityConfig” 或者 “usesCleartextTraffic” 这两种方式都能够

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="top.superbig.sof.shop_online_flutter">
    <!-- 增加网络拜访权限 -->
    <uses-permission android:name="android.permission.INTERNET" />
    <application
        android:icon="@mipmap/ic_launcher"
        android:label="Flutter 商城"
        android:networkSecurityConfig="@xml/network_security_config"
        android:usesCleartextTraffic="true"><!-- true 表明运用明文传输数据 -->
        <activity
            android:name=".MainActivity"
            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
            android:hardwareAccelerated="true"
            android:launchMode="singleTop"
            android:theme="@style/LaunchTheme"
            android:windowSoftInputMode="adjustResize">
            <!-- Specifies an Android theme to apply to this Activity as soon as
                 the Android process has started. This theme is visible to the user
                 while the Flutter UI initializes. After that, this theme continues
                 to determine the Window background behind the Flutter UI. -->
            <meta-data
                android:name="io.flutter.embedding.android.NormalTheme"
                android:resource="@style/NormalTheme" />
            <!-- Displays an Android View that continues showing the launch screen
                 Drawable until Flutter paints its first frame, then this splash
                 screen fades out. A splash screen is useful to avoid any visual
                 gap between the end of Android's launch screen and the painting of
                 Flutter's first frame. -->
            <meta-data
                android:name="io.flutter.embedding.android.SplashScreenDrawable"
                android:resource="@drawable/launch_background" />
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <!-- Don't delete the meta-data below.
             This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
        <meta-data
            android:name="flutterEmbedding"
            android:value="2" />
    </application>
</manifest>

network_security_config.xml 在 res 目录下的 xml 目录中,没有 xml 则新建一个即可 network_security_config.xml 的内容如下

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <base-config cleartextTrafficPermitted="true">
        <trust-anchors>
            <certificates src="system" /><!-- 表明信赖所有,如果只需求其间部分恳求能够明文传输能够运用一下-->
            <domain includeSubdomains="true">这里填写域名</domain>
        </trust-anchors>
    </base-config>
</network-security-config>

b、IOS 装备网络恳求权限

设置图中属性值为 YES 即可

Flutter 实现电商 APP 之 网络请求的封装与数据解析(三)

3、封装

代码如下

class HttpRequest{
  String _baseUrl;
  bool _needProxy = false;
  bool _needCert = false;
  ///dio 通用装备
  static BaseOptions optionParams = BaseOptions(
    connectTimeout: 15000,
    receiveTimeout: 10000,
    sendTimeout: 15000,
  );
  HttpRequest.create();
  ///设置baseUrl
  HttpRequest setBaseUrl(String baseUrl){
    _baseUrl = baseUrl;
    return this;
  }
  ///是否需求署理
  HttpRequest setNeedProxy(bool value) {
    _needProxy = value;
    return this;
  }
  ///是否需求校验证书
  HttpRequest setNeedCert(bool value){
    _needCert = value;
    return this;
  }
  String _getUrl(path){
    return _baseUrl==null?(Config.BASE_URL+path):(_baseUrl+path);
  }
  /// get 恳求
  /// [path] 恳求地址
  /// [params] 恳求参数
  /// [header] 恳求header
  Future<ResultDataEntity<T>> get<T>(String path,{Map<String,dynamic> params,Map header}) async{
    var url = _getUrl(path);
    return await request<T>(url, params, header, Options(method: "GET"));
  }
  /// post application/json 恳求
  /// [path] 恳求地址
  /// [params] 恳求参数-拼接在Url 后
  /// [data] body 传参数
  /// [header] 恳求header
  Future<ResultDataEntity<T>> postJson<T>(String path,{Map<String,dynamic> params,data,Map header}) async{
    var url = _getUrl(path);
    return await request<T>(url, params, header, Options(method: "POST",contentType: Headers.jsonContentType),data: data);
  }
  /// post application/x-www-form-urlencoded 恳求
  /// [path] 恳求地址
  /// [params] 恳求参数-拼接在Url 后
  /// [data] body 传参数
  /// [header] 恳求header
  Future<ResultDataEntity<T>> postUrl<T>(String path,{Map<String,dynamic> params,data,Map header}) async{
    var url = _getUrl(path);
    return await request<T>(url, params, header, Options(method: "POST",contentType: Headers.formUrlEncodedContentType),data: data);
  }
  /// post 表单恳求
  /// [path] 恳求地址
  /// [params] 恳求参数-拼接在Url 后
  /// [data] body 传参数
  /// [header] 恳求header
  Future<ResultDataEntity<T>> postForm<T>(String path,{Map<String,dynamic> params,data,Map header}) async{
    var url = _getUrl(path);
    var formData = FormData.fromMap(data);
    return await request<T>(url, params, header, Options(method: "POST"),data: formData);
  }
  Future<ResultDataEntity<T>> request<T>(String url,Map<String,dynamic> params,Map<String,String> header,Options options,{data}) async {
    var connectivityResult = await (new Connectivity().checkConnectivity());
    if(connectivityResult == ConnectivityResult.none){//判读网络链接状况
      return Future((){
        return  ResultDataEntity<T>.all(Code.SUCCESS,"网络未链接",null);
      });
    }
    if(!url.startsWith("https://") && !url.startsWith("http://")){//判读域名是否正确
      return Future((){
        return  ResultDataEntity<T>.all(Code.REQUEST_ERROR,"恳求地址过错",null);
      });
    }
    //设置header
    Map<String,String> headers = new HashMap();
    if(header != null){
      headers.addAll(header);
    }
    if(options != null){
      options.headers = headers;
    }else{
      options = new Options(method: "GET");
      options.headers = headers;
    }
    //增加默认公共参数
    if(params != null){
      params.addAll(getCommonParams());
    }else{
      params = getCommonParams();
    }
    Dio dio = new Dio();
    dio.options = optionParams;//设置公共参数
    (dio.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate = getHttpClientProxy();//设置网络恳求署理证书等
    //增加拦截器
    if(Config.DEBUG){//增加拦截器
      dio.interceptors.add(LogInterceptors());
    }
    Response response;
    try{
      response = await dio.request<String>(url,data:data,queryParameters: params,options: options);
    }on DioError catch(e){//过错方式处理
      switch(e.type){
        case DioErrorType.connectTimeout:
        case DioErrorType.sendTimeout:
        case DioErrorType.receiveTimeout:
          return Future((){return new ResultDataEntity.all(Code.NETWORK_TIMEOUT, "恳求超时", null);});
        case DioErrorType.cancel:
          return Future((){return new  ResultDataEntity.all(Code.REQUEST_CANCEL, "恳求撤销", null);});
        case DioErrorType.response:
          return Future((){return new  ResultDataEntity.all(e.response.statusCode, e.message, null);});
        default:
          return Future((){return new  ResultDataEntity.all(Code.UNKNOWN, e.message, null);});
      }
    }on Error catch(e){
      return Future((){return new  ResultDataEntity.all(Code.UNKNOWN, e.toString(), null);});
    }
    try{
      if(response.statusCode == 200){//恳求成功,解析数据
        String data = response.data;
        ResultDataEntity<T> result = ResultDataEntity();
        result.error_code = Code.SUCCESS;
        var jsonMap = json.decode(data);
        result.reason = "success";
        if(T == String){
          result.result = data as T;
        }else {
          result.result = JsonConvert.fromJsonAsT<T>(jsonMap);
        }
        return Future((){return result;});
      }
    }catch(e){
      return Future((){return ResultDataEntity.all(Code.SUCCESS, "success", response.data);});
    }
    return Future((){return new ResultDataEntity.all(Code.UNKNOWN, "不知道过错", null);});
  }
  getHttpClientProxy(){
    return (HttpClient client){
      if(_needProxy) {
        client.findProxy = (uri) {
          return 'PROXY localhost:8888';//设置网络署理
        };
      }
      //装备 Https 证书校验
      client.badCertificateCallback = (X509Certificate cert,String host,int port){
        if(_needCert){
          if(cert.pem == Config.HTTPS_CERT_PEM){
            return true;
          }else{
            return false;
          }
        }else{
          return true;
        }
      };
    };
  }
  getToken() async{
    String token = await SpUtils.get(Config.TOKEN_KEY);
    return token;
  }
  static Map<String,dynamic> getCommonParams(){
    var data = {
      "app":1,
      "token":"176031ae-f6be-4d75-be67-6d85e1d0142f"
    };
    return data;
  }
}

基础数据的封装

class ResultDataEntity<T> {
 int error_code;
 String reason;
 T result;
 ResultDataEntity();
 ResultDataEntity.all(this.error_code,this.reason,this.result);
}

关于数据的解析,运用的是 FlutterJsonBeanFactory 插件,传入 json 字符串主动生成 bean 对象,包含 json 解析的代码

3、运用

var data = await HttpRequest.create().get<IconsEntity>(Api.APP_ICON,params: {"placeId":1});