Flutter之Dio封装+实例(自己梳理)

2023-10-29 08:59
文章标签 封装 实例 梳理 flutter dio

本文主要是介绍Flutter之Dio封装+实例(自己梳理),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

参考链接

https://github.com/cfug/dio/blob/main/dio/README-ZH.md

添加依赖

 手动添加到pubspec.yaml:

dependencies:dio: ^替换为最新版本

在终端使用以下命令:

$ dart pub add dio

Dio

dio 是一个强大的 HTTP 网络请求库,支持全局配置、Restful API、FormData、拦截器、 请求取消、Cookie 管理、文件上传/下载、超时、自定义适配器、转换器等。

 使用单例模式封装网络工具类

单例模式详见:Flutter之单例模式的四种方法_YUFENGSHI.LJ的博客-CSDN博客

class HttpManager{//1、通过静态方法 getInstance() 访问实例—————— getInstance() 构造、获取、返回实例/*通过工厂方法获取该类的实例,将实例对象按对应的方法返回出去*实例不存在时,调用命名构造方法获取一个新的实例 */static HttpManager getInstance(){if(_instance==null){_instance=HttpManager._internal();}return _instance!;}//2、静态属性——该类的实例static HttpManager? _instance=HttpManager._internal();//3、私有的命名构造函数,确保外部不能拿到它————初始化实例HttpManager._internal(){}//4.1、创建一个 Dio 实例late Dio dio;

创建一个Dio实例,并初始化

可以使用默认配置或传递一个可选 BaseOptions参数来创建一个Dio实例

请求配置

BaseOptions 描述的是 Dio 实例发起网络请求的的公共配置,而 Options 描述了每一个Http请求的配置信息,每一次请求都可以单独配置。

单次请求的 Options 中的配置信息可以覆盖 BaseOptions 中的配置。

BaseOptions :

基类请求配置

//请求方式
String? method,//连接超时时间
Duration? connectTimeout,//接收超时
Duration? receiveTimeout,//发送超时
Duration? sendTimeout,//基本网址
String baseUrl = '',//请求包头
Map<String, dynamic>? headers,//以何种方式接收响应数据,默认是json
ResponseType? responseType = ResponseType.json,//内容类型
String? contentType,Map<String, dynamic>? queryParameters,Map<String, dynamic>? extra,   ValidateStatus? validateStatus,bool? receiveDataWhenStatusError,bool? followRedirects,int? maxRedirects,bool? persistentConnection,RequestEncoder? requestEncoder,ResponseDecoder? responseDecoder,ListFormat? listFormat,

Options : 

单次请求配置

/// 请求方式。
String method;/// 请求基本地址,可以包含路径例如 https://dart.dev/api/。
String? baseUrl;/// HTTP 请求头。
Map<String, dynamic>? headers;/// 连接服务器超时时间.
Duration? connectTimeout;/// 两次数据流数据接收的最长间隔时间,注意不是请求的最长接收时间。
Duration? receiveTimeout;/// 请求内容体,可以是任意类型。
dynamic data;/// 请求路径,如果以 http(s)开始, 则 [baseURL] 会被忽略,
/// 否则将会和 [baseUrl] 拼接出完整的地址。
String path = '';/// 请求的 Content-Type。
///
/// 默认值会由 [ImplyContentTypeInterceptor] 根据请求载荷类型进行推导。
/// 可以调用 [Interceptors.removeImplyContentTypeInterceptor] 进行移除。
///
/// 如果你想以 `application/x-www-form-urlencoded` 格式编码请求数据,
/// 可以设置此选项为 `Headers.formUrlEncodedContentType`,
/// [Dio] 会自动编码请求体。
String? contentType;/// 期望以哪种格式(方式)接受响应数据,包括 `json`、`stream` 和 `plain`。
///
/// 默认值是 `json`, 当响应头中 content-type 为 `application/json` 时,
/// dio 会自动将响应内容转化为 json 对象。
/// 如果想以二进制方式接受响应数据,如下载一个二进制文件,那么可以使用 `stream`。
///
/// 如果想以文本(字符串)格式接收响应数据,请使用 `plain`。
ResponseType? responseType;/// `validateStatus` 决定 HTTP 响应状态码是否被视为请求成功,
/// 返回 `true` 请求结果就会按成功处理,否则会按失败处理.
ValidateStatus? validateStatus;/// 用户自定义字段,可以在 [Interceptor]、[Transformer] 和 [Response] 中依次传递。
Map<String, dynamic>? extra;/// 请求地址的参数。
Map<String, dynamic /*String|Iterable<String>*/ >? queryParameters;/// 请求数据中数组的编码的方式,默认值为 `multiCompatible`。
ListFormat? listFormat;

RequestOptions:

实际请求配置,即[BaseOptions]和[Options]组合后的最终结果。 

/// The internal request option class that is the eventual result after
/// [BaseOptions] and [Options] are composed.
class RequestOptions extends _RequestConfig with OptionsMixin {RequestOptions({this.path = '',this.data,this.onReceiveProgress,this.onSendProgress,this.cancelToken,String? method,Duration? sendTimeout,Duration? receiveTimeout,Duration? connectTimeout,Map<String, dynamic>? queryParameters,String? baseUrl,Map<String, dynamic>? extra,Map<String, dynamic>? headers,ResponseType? responseType,String? contentType,ValidateStatus? validateStatus,bool? receiveDataWhenStatusError,bool? followRedirects,int? maxRedirects,bool? persistentConnection,RequestEncoder? requestEncoder,ResponseDecoder? responseDecoder,ListFormat? listFormat,bool? setRequestContentTypeWhenNoPayload,StackTrace? sourceStackTrace,})  : assert(connectTimeout == null || !connectTimeout.isNegative),super(method: method,sendTimeout: sendTimeout,receiveTimeout: receiveTimeout,extra: extra,headers: headers,responseType: responseType,contentType: contentType,validateStatus: validateStatus,receiveDataWhenStatusError: receiveDataWhenStatusError,followRedirects: followRedirects,maxRedirects: maxRedirects,persistentConnection: persistentConnection,requestEncoder: requestEncoder,responseDecoder: responseDecoder,listFormat: listFormat,) {this.sourceStackTrace = sourceStackTrace ?? StackTrace.current;this.queryParameters = queryParameters ?? {};this.baseUrl = baseUrl ?? '';this.connectTimeout = connectTimeout;}/// Create a [RequestOptions] from current instance with merged attributes.RequestOptions copyWith({String? method,Duration? sendTimeout,Duration? receiveTimeout,Duration? connectTimeout,dynamic data,String? path,Map<String, dynamic>? queryParameters,String? baseUrl,ProgressCallback? onReceiveProgress,ProgressCallback? onSendProgress,CancelToken? cancelToken,Map<String, dynamic>? extra,Map<String, dynamic>? headers,ResponseType? responseType,String? contentType,ValidateStatus? validateStatus,bool? receiveDataWhenStatusError,bool? followRedirects,int? maxRedirects,bool? persistentConnection,RequestEncoder? requestEncoder,ResponseDecoder? responseDecoder,ListFormat? listFormat,bool? setRequestContentTypeWhenNoPayload,}) {final contentTypeInHeader = headers != null &&headers.keys.map((e) => e.toLowerCase()).contains(Headers.contentTypeHeader);assert(!(contentType != null && contentTypeInHeader),'You cannot set both contentType param and a content-type header',);final ro = RequestOptions(method: method ?? this.method,sendTimeout: sendTimeout ?? this.sendTimeout,receiveTimeout: receiveTimeout ?? this.receiveTimeout,connectTimeout: connectTimeout ?? this.connectTimeout,data: data ?? this.data,path: path ?? this.path,baseUrl: baseUrl ?? this.baseUrl,queryParameters: queryParameters ?? Map.from(this.queryParameters),onReceiveProgress: onReceiveProgress ?? this.onReceiveProgress,onSendProgress: onSendProgress ?? this.onSendProgress,cancelToken: cancelToken ?? this.cancelToken,extra: extra ?? Map.from(this.extra),headers: headers ?? Map.from(this.headers),responseType: responseType ?? this.responseType,validateStatus: validateStatus ?? this.validateStatus,receiveDataWhenStatusError:receiveDataWhenStatusError ?? this.receiveDataWhenStatusError,followRedirects: followRedirects ?? this.followRedirects,maxRedirects: maxRedirects ?? this.maxRedirects,persistentConnection: persistentConnection ?? this.persistentConnection,requestEncoder: requestEncoder ?? this.requestEncoder,responseDecoder: responseDecoder ?? this.responseDecoder,listFormat: listFormat ?? this.listFormat,sourceStackTrace: sourceStackTrace,);if (contentType != null) {ro.headers.remove(Headers.contentTypeHeader);ro.contentType = contentType;} else if (!contentTypeInHeader) {ro.contentType = this.contentType;}return ro;}/// The source [StackTrace] which should always point to the invocation of/// [DioMixin.request] or if not provided, to the construction of the/// [RequestOptions] instance. In both instances the source context should/// still be available before it is lost due to asynchronous operations.@internalStackTrace? sourceStackTrace;/// Generate the requesting [Uri] from the options.Uri get uri {String url = path;if (!url.startsWith(RegExp(r'https?:'))) {url = baseUrl + url;final s = url.split(':/');if (s.length == 2) {url = '${s[0]}:/${s[1].replaceAll('//', '/')}';}}final query = Transformer.urlEncodeQueryMap(queryParameters, listFormat);if (query.isNotEmpty) {url += (url.contains('?') ? '&' : '?') + query;}// Normalize the url.return Uri.parse(url).normalizePath();}/// Request data in dynamic types.dynamic data;/// Defines the path of the request. If it starts with "http(s)",/// [baseUrl] will be ignored. Otherwise, it will be combined and resolved/// with the [baseUrl].String path;/// {@macro dio.CancelToken}CancelToken? cancelToken;/// {@macro dio.options.ProgressCallback}ProgressCallback? onReceiveProgress;/// {@macro dio.options.ProgressCallback}ProgressCallback? onSendProgress;
}

初始化DIO 

 /*在私有构造方法中,在里面可以进行初始化dio实例*/HttpManager._internal(){//4.2、设置BaseOptionsBaseOptions baseOptions=BaseOptions(//基本网址baseUrl:"https://lionstock-uat-new.chinaeast2.cloudapp.chinacloudapi.cn:8200/",//连接超时connectTimeout: Duration(milliseconds: 30000),//接收超时receiveTimeout: Duration(milliseconds: 5000),//包头headers: {"Content-Type": "application/json;Charset=UTF-8","connect":"get"},//内容类型contentType: 'application/json;Charset=UTF-8',//响应类型——期待已那种方式接收数据,默认是jsonresponseType: ResponseType.json,);//4.3 初始化dio实例dio=new Dio(baseOptions) ;//添加一个拦截器dio.interceptors.add(new DioLogInterceptor());}

响应数据 Response

当请求成功时会返回一个Response对象,它包含如下字段: 

/// 响应数据。可能已经被转换了类型, 详情请参考 [ResponseType]。
T? data;/// 响应对应的实际请求配置。
RequestOptions requestOptions;/// 响应的 HTTP 状态码。
int? statusCode;/// 响应对应状态码的详情信息。
String? statusMessage;/// 响应是否被重定向
bool isRedirect;/// 请求连接经过的重定向列表。如果请求未经过重定向,则列表为空。
List<RedirectRecord> redirects;/// 在 [RequestOptions] 中构造的自定义字段。
Map<String, dynamic> extra;/// 响应对应的头数据(响应头)
Headers headers;

Get方法

get方法中只有路径是必填的,

Future<Response<T>> get<T>(String path, {Object? data,Map<String, dynamic>? queryParameters,Options? options,CancelToken? cancelToken,ProgressCallback? onReceiveProgress,});

get方法使用指定的路径和查询参数向服务器发送 HTTP GET 请求。它还允许您传递其他选项,例如标头、响应类型和超时。该方法返回一个 Future 对象,该对象使用包含来自服务器的 HTTP 响应的 Response 对象进行解析。

封装get方法

get(String url,{option,params}) async {Response response;try{response=await dio.get(url,options: Options(responseType: ResponseType.json));print("response.data:${response.data}");print("response.data:${response.statusCode}");print("response.data:${response.statusMessage}");print("response.data:${response.headers}");}on Exception catch(e){print("Get方法出错:${e.toString()}");}}

get请求无参数

 接口:https://api.github.com/orgs/flutterchina/repos 

HttpManager.getInstance().get("https://reqres.in/api/users",option: Options(responseType: ResponseType.plain),);

 get请求有参数

在请求链接中拼接参数

   // 获取id 法一HttpManager.getInstance().get("https://reqres.in/api/users/1");// 获取id 法二HttpManager.getInstance().get("https://reqres.in/api/users?id=2");

通过 queryParameters 配制参数 

   // 获取id 法三 用参数类型Map<String,dynamic> map = Map();map["id"]= 3;HttpManager.getInstance().get("https://reqres.in/api/users",option: Options(responseType: ResponseType.json),params:map);

 Post方法 

在发出 GET 请求时,我们通常不传递任何数据。但是当发出 POST、PUT、DELETE 等请求时,我们需要传递正文/数据。

post方法与方法类似get,但增加了一个data参数,该参数代表请求正文。使用该方法获取请求标头getAuthorizationHeader并与提供的任何选项合并。使用发出请求dio.post,如果成功则返回响应数据。如果请求由于 Dio 错误而失败,ErrorEntity则会抛出异常。

封装post方法

post(api,{params}) async {Response response;//请求参数 为空时,配置if(params==null){params["marketNo"] = "PC_Flutter";params["versionNo"] = '10105';/*版本号*/params["token"] = '6b2fc908787c428ab16559fce9d86bf2';params["uid"] = '201323';}try{response=await dio.post(api,queryParameters: params,);print("post response:${response.data}\n");}on Exception catch (e){print("post出错:${e.toString()}");}}

利用post方法注册一个用户

HttpManager.getInstance().post("https://www.wanandroid.com/user/register",params: {"username": "zsdhwiehfwo","password": "123456","repassword": "123456"});

打印结果: 

post response:{data: {admin: false, chapterTops: [], coinCount: 0, collectIds: [], email: , icon: , id: 151550, nickname: zsdhwiehfwo, password: , publicName: zsdhwiehfwo, token: , type: 0, username: zsdhwiehfwo}, errorCode: 0, errorMsg: }

拦截器

每个 Dio 实例都可以添加任意多个拦截器,他们会组成一个队列,拦截器队列的执行顺序是先进先出。 通过使用拦截器,你可以在请求之前、响应之后和发生异常时(未被 then 或 catchError 处理) 做一些统一的预处理操作。

拦截器处理器

handler.next

/// Deliver the [response] to the next interceptor.
///
/// Typically, the method should be called once interceptors done
/// manipulating the [response].
///将[响应]传递给下一个拦截器。通常,一旦拦截器完成操作[响应],就应该调用该方法。void next(Response response) {_completer.complete(InterceptorState<Response>(response),);_processNextInQueue?.call();}

如果不调用handler.next(response)方法,那么请求将被中止,也就是说后续的拦截器和回调函数将不会被执行。 

请求前 onRequest 

/// Called when the request is about to be sent.void onRequest(RequestOptions options,RequestInterceptorHandler handler,) {handler.next(options);}
  • RequestOptions options,表示本次请求的实际配置

  • RequestInterceptorHandler是一个拦截器处理器,用于处理请求拦截器中的逻辑。

/// The handler for interceptors to handle after respond.
class ResponseInterceptorHandler extends _BaseHandler {/// Deliver the [response] to the next interceptor.////// Typically, the method should be called once interceptors done/// manipulating the [response].void next(Response response) {_completer.complete(InterceptorState<Response>(response),);_processNextInQueue?.call();}/// Completes the request by resolves the [response] as the result.void resolve(Response response) {_completer.complete(InterceptorState<Response>(response,InterceptorResultType.resolve,),);_processNextInQueue?.call();}/// Completes the request by reject with the [error] as the result.////// Invoking the method will make the rest of interceptors in the queue/// skipped to handle the request,/// unless [callFollowingErrorInterceptor] is true/// which delivers [InterceptorResultType.rejectCallFollowing]/// to the [InterceptorState].void reject(DioException error,[bool callFollowingErrorInterceptor = false]) {_completer.completeError(InterceptorState<DioException>(error,callFollowingErrorInterceptor? InterceptorResultType.rejectCallFollowing: InterceptorResultType.reject,),error.stackTrace,);_processNextInQueue?.call();}
}
  • handler.next(options)方法将处理后的请求传递给下一个拦截器或者最终的请求回调函数。如果不调用handler.next(options)方法,那么请求将被中止,也就是说后续的拦截器和回调函数将不会被执行。
  • handler.resolve(options)表示直接将请求返回给请求回调函数
  • handler.reject(error)表示将错误信息返回给请求回调函数等

响应前 onResponse

/// Called when the response is about to be resolved.
///当响应即将解决时调用。void onResponse(Response response,ResponseInterceptorHandler handler,) {handler.next(response);}
  • response表示响应数据,包括响应状态码、响应头、响应数据等

  • ResponseInterceptorHandler是一个拦截器处理器,用于处理响应拦截器中的逻辑。

/// The handler for interceptors to handle after respond.
class ResponseInterceptorHandler extends _BaseHandler {/// Deliver the [response] to the next interceptor.////// Typically, the method should be called once interceptors done/// manipulating the [response].void next(Response response) {_completer.complete(InterceptorState<Response>(response),);_processNextInQueue?.call();}/// Completes the request by resolves the [response] as the result.void resolve(Response response) {_completer.complete(InterceptorState<Response>(response,InterceptorResultType.resolve,),);_processNextInQueue?.call();}/// Completes the request by reject with the [error] as the result.////// Invoking the method will make the rest of interceptors in the queue/// skipped to handle the request,/// unless [callFollowingErrorInterceptor] is true/// which delivers [InterceptorResultType.rejectCallFollowing]/// to the [InterceptorState].void reject(DioException error,[bool callFollowingErrorInterceptor = false]) {_completer.completeError(InterceptorState<DioException>(error,callFollowingErrorInterceptor? InterceptorResultType.rejectCallFollowing: InterceptorResultType.reject,),error.stackTrace,);_processNextInQueue?.call();}
}
  • 在响应拦截器中,我们可以通过 handler.next(response) 方法将响应传递给下一个拦截器或者最终的请求回调函数。如果不调用handler.next(response)方法,那么请求将被中止,也就是说后续的拦截器和回调函数将不会被执行。
  • handler.resolve(response)表示直接将响应返回给请求回调函数
  • handler.reject(error)表示将错误信息返回给请求回调函数等。

异常时 onError

/// Called when an exception was occurred during the request.
///当请求过程中发生异常时调用。void onError(DioException err,ErrorInterceptorHandler handler,) {handler.next(err);}
  • DioException 表示异常信息,包括错误类型、错误消息、错误堆栈等信息;
  • ErrorInterceptorHandler 拦截器的处理程序用于处理请求期间发生的错误。 

自定义拦截器:

class DioLogInterceptor extends Interceptor{///请求前@overrideFuture onRequest(RequestOptions options, RequestInterceptorHandler handler) async {String requestStr = "\n==================== 请求前拦截——REQUEST ====================\n""- URL:\n${options.baseUrl + options.path}\n""- METHOD: ${options.method}\n";requestStr += "- HEADER:\n${options.headers.mapToStructureString()}\n";final data = options.data;if (data != null) {if (data is Map)requestStr += "- BODY:\n${data.mapToStructureString()}\n";else if (data is FormData) {final formDataMap = Map()..addEntries(data.fields)..addEntries(data.files);requestStr += "- BODY:\n${formDataMap.mapToStructureString()}\n";} elserequestStr += "- BODY:\n${data.toString()}\n";}print(requestStr);return handler.next(options);}}

添加拦截器

//添加一个拦截器dio.interceptors.add(new DioLogInterceptor());

错误处理

 当请求过程中发生错误时, Dio 会将 Error/Exception 包装成一个 DioException:

这篇关于Flutter之Dio封装+实例(自己梳理)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/299613

相关文章

【机器学习】高斯过程的基本概念和应用领域以及在python中的实例

引言 高斯过程(Gaussian Process,简称GP)是一种概率模型,用于描述一组随机变量的联合概率分布,其中任何一个有限维度的子集都具有高斯分布 文章目录 引言一、高斯过程1.1 基本定义1.1.1 随机过程1.1.2 高斯分布 1.2 高斯过程的特性1.2.1 联合高斯性1.2.2 均值函数1.2.3 协方差函数(或核函数) 1.3 核函数1.4 高斯过程回归(Gauss

Flutter 进阶:绘制加载动画

绘制加载动画:由小圆组成的大圆 1. 定义 LoadingScreen 类2. 实现 _LoadingScreenState 类3. 定义 LoadingPainter 类4. 总结 实现加载动画 我们需要定义两个类:LoadingScreen 和 LoadingPainter。LoadingScreen 负责控制动画的状态,而 LoadingPainter 则负责绘制动画。

C++操作符重载实例(独立函数)

C++操作符重载实例,我们把坐标值CVector的加法进行重载,计算c3=c1+c2时,也就是计算x3=x1+x2,y3=y1+y2,今天我们以独立函数的方式重载操作符+(加号),以下是C++代码: c1802.cpp源代码: D:\YcjWork\CppTour>vim c1802.cpp #include <iostream>using namespace std;/*** 以独立函数

实例:如何统计当前主机的连接状态和连接数

统计当前主机的连接状态和连接数 在 Linux 中,可使用 ss 命令来查看主机的网络连接状态。以下是统计当前主机连接状态和连接主机数量的具体操作。 1. 统计当前主机的连接状态 使用 ss 命令结合 grep、cut、sort 和 uniq 命令来统计当前主机的 TCP 连接状态。 ss -nta | grep -v '^State' | cut -d " " -f 1 | sort |

JavaSE——封装、继承和多态

1. 封装 1.1 概念      面向对象程序三大特性:封装、继承、多态 。而类和对象阶段,主要研究的就是封装特性。何为封装呢?简单来说就是套壳屏蔽细节 。     比如:对于电脑这样一个复杂的设备,提供给用户的就只是:开关机、通过键盘输入,显示器, USB 插孔等,让用户来和计算机进行交互,完成日常事务。但实际上:电脑真正工作的却是CPU 、显卡、内存等一些硬件元件。

Java Websocket实例【服务端与客户端实现全双工通讯】

Java Websocket实例【服务端与客户端实现全双工通讯】 现很多网站为了实现即时通讯,所用的技术都是轮询(polling)。轮询是在特定的的时间间隔(如每1秒),由浏览器对服务器发 出HTTP request,然后由服务器返回最新的数据给客服端的浏览器。这种传统的HTTP request 的模式带来很明显的缺点 – 浏 览器需要不断的向服务器发出请求,然而HTTP

Flutter Button使用

Material 组件库中有多种按钮组件如ElevatedButton、TextButton、OutlineButton等,它们的父类是于ButtonStyleButton。         基本的按钮特点:         1.按下时都会有“水波文动画”。         2.onPressed属性设置点击回调,如果不提供该回调则按钮会处于禁用状态,禁用状态不响应用户点击。

哈希表的封装和位图

文章目录 2 封装2.1 基础框架2.2 迭代器(1)2.3 迭代器(2) 3. 位图3.1 问题引入3.2 左移和右移?3.3 位图的实现3.4 位图的题目3.5 位图的应用 2 封装 2.1 基础框架 文章 有了前面map和set封装的经验,容易写出下面的代码 // UnorderedSet.h#pragma once#include "HashTable.h"

梳理2024年,螺丝钉们爱用的3款剪辑软件

这年头,视频到处都是,就跟天上的星星一样数不清。不管你是公司里的新面孔,还是职场上的老狐狸,学会怎么剪视频,就好比找到了赢的秘诀。不管是给上司汇报工作,展示你的产品,还是自己搞点小视频记录生活,只要是剪辑得漂亮,肯定能一下子吸引大家的目光,让人记得你。咱们今天就来侃侃现在超火的三款视频剪辑工具,尤其是PR剪辑,你肯定听说过,这货在剪辑界可是大名鼎鼎,用它剪视频,既专业又麻利。 NO1. 福昕轻松

828华为云征文|华为云Flexus X实例docker部署rancher并构建k8s集群

828华为云征文|华为云Flexus X实例docker部署rancher并构建k8s集群 华为云最近正在举办828 B2B企业节,Flexus X实例的促销力度非常大,特别适合那些对算力性能有高要求的小伙伴。如果你有自建MySQL、Redis、Nginx等服务的需求,一定不要错过这个机会。赶紧去看看吧! 什么是华为云Flexus X实例 华为云Flexus X实例云服务是新一代开箱即用、体