Flutter学习日记之Http&Dio网络请求的使用与封装

Posted Android_小黑

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Flutter学习日记之Http&Dio网络请求的使用与封装相关的知识,希望对你有一定的参考价值。

本文地址:https://blog.csdn.net/qq_40785165/article/details/117622514,转载需附上此地址

大家好,我是小黑,一个还没秃头的程序员~~~

人生的路无需苛求,只要你迈步,路就在你的脚下延伸。

今天分享的内容是Flutter中关于网络数据的请求–Http/Dio的使用,源码地址:https://gitee.com/fjjxxy/flutter-study.git,效果如下:

不管是开发pc端还是移动端,都免不了请求服务器接口,今天介绍的就是两种网络访问的库,访问https://pub.dev/搜索即可,数据接口使用的是玩Android 开放API

  • Http
  • Dio

(一)Http的使用

1.添加依赖,在pubspec.yaml文件中配置库的依赖

dependencies:
  flutter:
    sdk: flutter


  # The following adds the Cupertino Icons font to your application.
  # Use with the CupertinoIcons class for ios style icons.
  cupertino_icons: ^0.1.3
  http: ^0.13.3

2.引入

import 'package:http/http.dart' as http;

3.get请求

get方法的参数如下:

参数说明
url访问路径
headers请求头

以下是按钮点击进行get请求,代码如下:

            TextButton(
              onPressed:() async {
                var url = Uri.parse(Api.MP_WECHAT_NAMES);
                var response = await http.get(url,headers: {"token":""});
                Toast.toast(context,
                    msg: jsonDecode(response.body.toString())["errorCode"] == 0
                        ? "请求成功"
                        : "请求失败");
              },
              style: ButtonStyle(
                  backgroundColor: MaterialStateProperty.all(Colors.red),
                  foregroundColor: MaterialStateProperty.all(Colors.white)),
              child: Text("get请求"),
            )

注:返回的数据是Json字符串,需要使用jsonDecode转换成Map

4.post请求

post方法的参数如下:

参数说明
url访问路径
headers请求头
body请求参数
encoding编码格式

这里按钮点击之后请求登录的接口,代码如下:

            TextButton(
              style: ButtonStyle(
                  backgroundColor: MaterialStateProperty.all(Colors.red),
                  foregroundColor: MaterialStateProperty.all(Colors.white)),
              onPressed: () async {
                var url = Uri.parse(Api.LOGIN);
                var response = await http.post(url,
                    body: {"username": "xiaohei", "password": "123456"});
                Toast.toast(context,
                    msg: jsonDecode(response.body.toString())["errorCode"] == 0
                        ? "请求成功"
                        : "请求失败");
              },
              child: Text("post请求"),
            )

Http的使用就介绍到这里,接下来我们介绍Dio的使用以及封装

(二)Dio的使用

dio是一个自主的Dart Http请求库,支持Restful API、FormData、拦截器、请求取消、Cookie管理、文件上传/下载、超时、自定义延迟等…

1.添加依赖

dependencies:
  flutter:
    sdk: flutter


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

2.引入

import 'package:dio/dio.dart';

3.get请求

get方法的参数如下:

参数说明
path请求路径
queryParameters请求体参数,类型是Map
options可进行一些值的配置,比如连接超时时间,基本路径等
cancelToken用于取消请求,类型是CancelToken,同一个cancelToken的请求会都被取消掉
onReceiveProgress接收进度的监听

简单使用的代码如下:

   response = await dio.get(url,
              queryParameters: parameters ?? new Map<String, dynamic>(),
              cancelToken: cancelToken);

4.post请求

post方法的参数如下:

参数说明
path请求路径
data请求体参数,类型是Map
queryParameters请求体参数,类型是Map
options可进行一些值的配置,比如连接超时时间,基本路径等
cancelToken用于取消请求,类型是CancelToken,同一个cancelToken的请求会都被取消掉
onReceiveProgress接收进度的监听
onSendProgress上传进度的监听

基本使用的代码如下:

  response = await dio.post(url,
              data: parameters ?? new Map<String, dynamic>(),
              cancelToken: cancelToken);

5.上传多个文件

上传文件用的也是post方法,基本使用的代码如下:onSendProgress是上传进度的监听

  uploadFiles(String url, List<String> list) async {
    var formData = FormData.fromMap({
      'files': list.map((e) {
        return MultipartFile.fromFileSync(e,
            filename: e.substring(e.indexOf("/")));
      })
    });
	  var response = await dio.post(url, data: formData,onSendProgress: (int sent, int total){
      print('$sent $total');
    });
	return response.data;
  }

上面使用FormData.fromMap创建文件数组会为key添加"[]",即文件数组的key为"files[]",这就是服务器端接收所判断的key,要想不自动加"[]",可以通过往FormData中添加MapEntry创建文件数组

  uploadFiles2(String url, List<String> list,onSuccess, onError) async {
    var formData = FormData();
    list.map((e) {
      formData.files.add(MapEntry(
        'files',
        MultipartFile.fromFileSync('./example/upload.txt',
            filename: 'upload.txt'),
      ));
    });
      var response = await dio.post(url, data: formData,onSendProgress: (int sent, int total){
      print('$sent $total');
    });
	return response.data;
  }

6.下载文件

下载文件使用的是Dio的download方法,部分参数如下:

参数说明
urlPath下载的路径
savePath保存的路径
onReceiveProgress下载进度的监听

基本的使用代码如下:

  downloadFile(urlPath, savePath, onReceiveProgress) async {
    Response response;
    try {
      response = await dio.download(urlPath, savePath,
          onReceiveProgress: onReceiveProgress);
    } on DioError catch (e) {
      //具体的错误类型可以自己处理
      print(e.message);
    }
    return response.data;
  }

Dio还提供了以下几个Api:

  • Future put(…)
  • Future delete(…)
  • Future head(…)
  • Future put(…)
  • Future path(…)
  • Future fetch(RequestOptions)

篇幅原因大家可以举一反三自己试试,以上那些Api都是一些Restful API,都是Future request() Api的别名

7.讲完了Api, 再介绍一下Dio怎么设置拦截器和公共配置的

已打印出请求信息为例,先定义一个拦截器,代码如下:

  static InterceptorsWrapper interceptorsWrapper() {
    return InterceptorsWrapper(onRequest: (options, handler) {
      // Do something before request is sent
      print(options.path);
      print(options.queryParameters);
      print(options.headers.toString());
      return handler.next(options); //continue
     
    }, onResponse: (response, handler) {
      // Do something with response data
      return handler.next(response); // continue
     
    }, onError: (DioError e, handler) {
      // Do something with response error
      return handler.next(e); //continue
     
    });
  }

添加拦截器,代码如下:

mDio.interceptors.add(interceptorsWrapper());

添加公共配置,BaseOptions用来配置公共配置,而上面所列举出来的Api中的options参数是用来单独配置每次请求时的配置的,单独的配置可以覆盖公共配置,这里配置一些时间和默认的请求头,添加拦截器和配置的完整代码如下:

  static Dio getDio() {
    options = BaseOptions(
      //请求基地址,可以包含子路径
      baseUrl: Api.BASE_URL,
      //连接服务器超时时间,单位是毫秒.
      connectTimeout: 5000,
      //2.x中为接收数据的最长时限
      receiveTimeout: 3000,
      //Http请求头.
      headers: {"token": ""},
      / 请求的Content-Type,默认值是"application/json; charset=utf-8".
      //   /// 如果您想以"application/x-www-form-urlencoded"格式编码请求数据,
      //   /// 可以设置此选项为 `Headers.formUrlEncodedContentType`,  这样[Dio]
      //   /// 就会自动编码请求体.
      contentType: Headers.jsonContentType,

      /// [responseType] 表示期望以那种格式(方式)接受响应数据。
      /// 目前 [ResponseType] 接受三种类型 `JSON`, `STREAM`, `PLAIN`.
      ///
      /// 默认值是 `JSON`, 当响应头中content-type为"application/json"时,dio 会自动将响应内容转化为json对象。
      /// 如果想以二进制方式接受响应数据,如下载一个二进制文件,那么可以使用 `STREAM`.
      ///
      /// 如果想以文本(字符串)格式接收响应数据,请使用 `PLAIN`.
      responseType: ResponseType.json,
    );
    mDio = Dio(options);
    mDio.interceptors.add(interceptorsWrapper());
    return mDio;
  }

8.别忘了还要统一管理接口路径,代码如下:

class Api {
  static final String BASE_URL = "https://www.wanandroid.com/";

  static final String MP_WECHAT_NAMES = BASE_URL + "wxarticle/chapters/json";
  static final String LOGIN = BASE_URL + "user/login";
}

(三)Dio的封装

上面Dio介绍的所有的Api都会返回一个Map类型或者字符串类型的response,但是每次都有自己去解析就很费事了,所以这里的封装就是为了将返回的数据自动转成自己想要的类型,方便直接调用里面的字段进行界面绘制,也要让这个请求自动进行成功回调以及失败回调。

1.json解析成dart class类(实体类)

这里的解析使用到的是FlutterJsonBeanFactory插件,在AndroidStudio-Settings-Plugins-Marketplace中下载插件后,在你想要创建文件的文件夹上右键-new-JsonToDartBeanAction,输入json数据和类名,点击"make",即可创建具有相应字段的类,如下图所示:

创建一个最外层的实体类,里面的data定义为泛型,可以将数据放进泛型中,最终解析成自己想要的实体类型,后面就方便调用字段了,BaseBean的代码如下:

class BaseBean<T> {
  T data;
  int errorCode;
  String errorMsg;

  BaseBean({this.data, this.errorCode, this.errorMsg});

  BaseBean.fromJson(Map<String, dynamic> json) {
    if (json['data'] != null && json['data'] != 'null') {
      data = JsonConvert.fromJsonAsT<T>(json['data']);
    }
    errorCode = json['errorCode'];
    errorMsg = json['errorMsg'];
  }

  Map<String, dynamic> toJson() {
    final Map<String, dynamic> data = new Map<String, dynamic>();
    if (this.data != null) {
      data['data'] = this.data;
    }
    data['errorCode'] = this.errorCode;
    data['errorMsg'] = this.errorMsg;
    print(data is Map);
    return data;
  }
}

泛型解析的代码如下:

  if (response.statusCode == 200) {
        /// 将后台的data字段转成自己想要的数据/数据集,code根据后端实际返回进行判断访问结果
        BaseBean<T> bean = BaseBean.fromJson(response.data);
        if (bean.errorCode == 0 && onSuccess != null) {
          /// 返回BaseBean,里面的data是自己想要的数据
          onSuccess(bean);
        } else {
          onError(bean.errorMsg);
        }
      } else {
        throw Exception('${response.statusCode}+${response.statusMessage}');
      }
  BaseBean.fromJson(Map<String, dynamic> json) {
    if (json['data'] != null && json['data'] != 'null') {
      data = JsonConvert.fromJsonAsT<T>(json['data']);
    }
    errorCode = json['errorCode'];
    errorMsg = json['errorMsg'];
  }

2.泛型解析介绍完了,封装工具类中其他的代码根据上面讲的那些Api的内容,相信大家都看得懂,这里就不再详细说明了,封装类HttpHelper 的完整代码如下:

import 'package:dio/dio.dart';

import 'Api.dart';
import 'BaseBean.dart';

class HttpHelper {
  static Dio mDio;
  static BaseOptions options;
  static HttpHelper httpHelper;

  CancelToken cancelToken = CancelToken();

  static const String GET = 'get';
  static const String POST = 'post';
  static const String PUT = 'put';
  static const String PATCH = 'patch';
  static const String DELETE = 'delete';

  static HttpHelper get instance => getInstance();

  static Dio get dio => getDio();

  static HttpHelper getInstance() {
    if (null == httpHelper) httpHelper = HttpHelper();
    return httpHelper;
  }

  static Dio getDio() {
    options = BaseOptions(
      //请求基地址,可以包含子路径
      baseUrl: Api.BASE_URL,
      //连接服务器超时时间,单位是毫秒.
      connectTimeout: 10000,
      //2.x中为接收数据的最长时限
      receiveTimeout: 5000,
      //Http请求头.
      headers: {"token": ""},
      / 请求的Content-Type,默认值是"application/json; charset=utf-8".
      //   /// 如果您想以"application/x-www-form-urlencoded"格式编码请求数据,
      //   /// 可以设置此选项为 `Headers.formUrlEncodedContentType`,  这样[Dio]
      //   /// 就会自动编码请求体.
      contentType: Headers.jsonContentType,

      /// [responseType] 表示期望以那种格式(方式)接受响应数据。
      /// 目前 [ResponseType] 接受三种类型 `JSON`, `STREAM`, `PLAIN`.
      ///
      /// 默认值是 `JSON`, 当响应头中content-type为"application/json"时,dio 会自动将响应内容转化为json对象。
      /// 如果想以二进制方式接受响应数据,如下载一个二进制文件,那么可以使用 `STREAM`.
      ///
      /// 如果想以文本(字符串)格式接收响应数据,请使用 `PLAIN`.
      responseType: ResponseType.json,
    );
    mDio = Dio(options);
    mDio.interceptors.add(interceptorsWrapper());
    return mDio;
  }

  static InterceptorsWrapper interceptorsWrapper() {
    return InterceptorsWrapper(onRequest: (options, handler) {
      // Do something before request is sent
      print(options.path);
      print(options.queryParameters);
      print(options.headers.toString());
      return handler.next(options); //continue
      // 如果你想完成请求并返回一些自定义数据,你可以resolve一个Response对象 `handler.resolve(response)`。
      // 这样请求将会被终止,上层then会被调用,then中返回的数据将是你的自定义response.
      //
      // 如果你想终止请求并触发一个错误,你可以返回一个`DioError`对象,如`handler.reject(error)`,
      // 这样请求将被中止并触发异常,上层catchError会被调用。
    }, onResponse: (response, handler) {
      // Do something with response data
      return handler.next(response); // continue
      // 如果你想终止请求并触发一个错误,你可以 reject 一个`DioError`对象,如`handler.reject(error)`,
      // 这样请求将被中止并触发异常,上层catchError会被调用。
    }, onError: (DioError e, handler) {
      // Do something with response error
      return handler.next(e); //continue
      // 如果你想完成请求并返回一些自定义数据,可以resolve 一个`Response`,如`handler.resolve(response)`。
      // 这样请求将会被终止,上层then会被调用,then中返回的数据将是你的自定义response.
    });
  }

  ///Get请求
  void getHttp<T>(
    String url, {
    parameters,
    cancelToken,
    Function(BaseBean<T> t) onSuccess,
    Function(String error) onError,
  }) async {
    try {
      getResponse<T>(url,
          method: GET,
          cancelToken: cancelToken,
          parameters: parameters,
          onSuccess: onSuccess,
          onError: onError);
    } catch (e) {
      print(e以上是关于Flutter学习日记之Http&Dio网络请求的使用与封装的主要内容,如果未能解决你的问题,请参考以下文章

Flutter学习日记之表单组件Radio单选框&Checkbox复选框的使用

Flutter学习日记之实现上拉加载&下拉刷新的Listview

Flutter学习日记之实现上拉加载&下拉刷新的Listview

Flutter学习日记之实现上拉加载&下拉刷新的Listview

Flutter学习日记之Chip标签组件的使用

Flutter学习-网络请求