Flutter学习日记之Http&Dio网络请求的使用与封装 您所在的位置:网站首页 dio网络请求实现原理 Flutter学习日记之Http&Dio网络请求的使用与封装

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

2024-07-11 16:06| 来源: 网络整理| 查看: 265

本文地址: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

HttpDio (一)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请求体参数,类型是Mapoptions可进行一些值的配置,比如连接超时时间,基本路径等cancelToken用于取消请求,类型是CancelToken,同一个cancelToken的请求会都被取消掉onReceiveProgress接收进度的监听

简单使用的代码如下:

response = await dio.get(url, queryParameters: parameters ?? new Map(), cancelToken: cancelToken); 4.post请求

post方法的参数如下:

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

基本使用的代码如下:

response = await dio.post(url, data: parameters ?? new Map(), cancelToken: cancelToken); 5.上传多个文件

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

uploadFiles(String url, List 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 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 data; int errorCode; String errorMsg; BaseBean({this.data, this.errorCode, this.errorMsg}); BaseBean.fromJson(Map json) { if (json['data'] != null && json['data'] != 'null') { data = JsonConvert.fromJsonAsT(json['data']); } errorCode = json['errorCode']; errorMsg = json['errorMsg']; } Map toJson() { final Map data = new Map(); 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 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 json) { if (json['data'] != null && json['data'] != 'null') { data = JsonConvert.fromJsonAsT(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( String url, { parameters, cancelToken, Function(BaseBean t) onSuccess, Function(String error) onError, }) async { try { getResponse(url, method: GET, cancelToken: cancelToken, parameters: parameters, onSuccess: onSuccess, onError: onError); } catch (e) { print(e); } } void postHttp( String url, { String method, parameters, Function(BaseBean t) onSuccess, Function(String error) onError, }) async { ///定义请求参数 parameters = parameters ?? Map(); getResponse(url, method: method ?? POST, parameters: parameters, onSuccess: onSuccess, onError: onError); } /* * 下载文件 */ downloadFile(urlPath, savePath, onReceiveProgress) async { Response response; try { response = await dio.download(urlPath, savePath, onReceiveProgress: onReceiveProgress); } on DioError catch (e) { formatError(e); } return response.data; } /* * 上传多个文件,key为files[] * v3.0.0 以后通过Formdata.fromMap()创建的Formdata,如果有文件数组,是默认会给key加上“[]”的 */ uploadFiles(String url, List list, onSuccess, onError) async { var formData = FormData.fromMap({ 'files': list.map((e) { return MultipartFile.fromFileSync(e, filename: e.substring(e.indexOf("/"))); }) }); getResponse(url, method: POST, parameters: formData, onSuccess: onSuccess, onError: onError); } uploadFiles2(String url, List list, onSuccess, onError) async { var formData = FormData(); list.map((e) { formData.files.add(MapEntry( 'files', MultipartFile.fromFileSync(e, filename: e.substring(e.indexOf("/"))))); }); getResponse(url, method: POST, parameters: formData, onSuccess: onSuccess, onError: onError); } void getResponse( String url, { String method, parameters, cancelToken, Function(BaseBean t) onSuccess, Function(String error) onError, }) async { try { //这里指定response自动转成map,不指定的话有可能是String类型 Response response; switch (method) { case GET: response = await dio.get(url, queryParameters: parameters ?? new Map(), cancelToken: cancelToken); break; case PUT: response = await dio.put(url, queryParameters: parameters ?? new Map(), cancelToken: cancelToken); break; case PATCH: response = await dio.patch(url, queryParameters: parameters ?? new Map(), cancelToken: cancelToken); break; case DELETE: response = await dio.delete(url, queryParameters: parameters ?? new Map(), cancelToken: cancelToken); break; default: response = await dio.post(url, data: parameters ?? new Map(), cancelToken: cancelToken); break; } //200代表网络请求成功 if (response.statusCode == 200) { /// 将后台的data字段转成自己想要的数据/数据集,code根据后端实际返回进行判断访问结果 BaseBean 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}'); } } catch (e) { print(e.toString()); onError(e.toString()); } } void formatError(DioError e) { print(e.message); } /* * 取消请求 * 同一个cancel token 可以用于多个请求,当一个cancel token取消时,所有使用该cancel token的请求都会被取消。 */ void cancelRequests(CancelToken token) { token.cancel("cancelled"); } } 3.调用

get请求,注意这里data解析的类型是List

void getData(BuildContext context) { CancelToken cancelToken = CancelToken(); HttpHelper.instance.getHttp(Api.MP_WECHAT_NAMES, cancelToken: cancelToken, onSuccess: (data) { setState(() { mList.clear(); mList.addAll(data.data); Toast.toast(context, msg: "请求成功"); }); }, onError: (message) { Toast.toast(context, msg: message); print("onError" + message); }); }

post请求

/// post请求 _postData(BuildContext context) { Map map = Map(); map.putIfAbsent("username", () => "123456"); map.putIfAbsent("password", () => "123456"); HttpHelper.instance.postHttp(Api.LOGIN, parameters: map, method: HttpHelper.POST, onSuccess: (data) { Toast.toast(context, msg: data.toString()); }, onError: (message) { Toast.toast(context, msg: message); }); }

到这里,Flutter中关于Http&Dio网络请求的内容就介绍完了,多写才能熟能生巧,感兴趣的小伙伴可以下载源码看一下,希望大家可以点个Star,支持一下小白的flutter学习经历,最后,希望喜欢我文章的朋友们可以帮忙点赞、收藏、评论,也可以关注一下,如果有问题可以在评论区提出,后面我会持续更新Flutter的学习记录,与大家分享,谢谢大家的支持与阅读!



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有