Flutter Dio 网络工具类封装
前言: Flutter 的网络请求,可以使用 Dart 原生网络请求 HttpClient,但是这样来就很多功能需要我们重新做一次轮子。所以一般来说,可以找个知名的第三方开源框架做封装使用。比如 Dio...
Flutter 引入 Dio 库
dio 是一个强大的 Dart Http请求库,支持 Restful API、FormData、拦截器、请求取消、Cookie 管理、文件上传/下载、超时、自定义适配器等...
打开 pub.dev/packages/di… 找到最新版本。
添加依赖
dependencies:
dio: ^3.x.x // 请使用 pub 上 3.0.0 分支的最新版本
极简的示例
import 'package:dio/dio.dart';
void getHttp() async {
try {
Response response = await Dio().get("http://www.baidu.com");
print(response);
} catch (e) {
print(e);
}
}
关于 Dio 详细的使用说明,可以看说明文档: github.com/flutterchin…
Dio 封装
为什么要封装
- 可以做一些公共处理,方便灵活使用。
- 方便以后换成别的第三方开源框架。
DioManager
单例网络工具类
import 'dart:convert';
import 'package:dio/dio.dart';
import 'package:flutter/cupertino.dart';
import 'package:smarthome/common/api.dart';
import 'package:smarthome/common/err_code.dart';
import 'package:smarthome/common/global.dart';
import 'package:smarthome/models/base.dart';
import 'package:smarthome/models/token.dart';
import 'package:smarthome/utils/sp_util.dart';
import 'package:smarthome/mlui/ml_toast.dart';
import 'package:smarthome/routes/routes.dart';
/// Dio 请求方法
enum DioMethod {
get,
post,
put,
delete,
}
/// 网络工具类
class DioManager {
// 单例
factory DioManager() =>_getInstance();
static DioManager _instance;
static DioManager _getInstance() {
if (_instance == null) {
_instance = DioManager._init();
}
return _instance;
}
Dio _dio;
/// 初始化
DioManager._init() {
if (_dio == null) {
// 设置 Dio 默认配置
_dio = Dio(BaseOptions(
// 请求基地址
baseUrl: Api.baseUrl,
// 连接服务器超时时间,单位是毫秒
connectTimeout: 60*1000,
// 接收数据的最长时限
receiveTimeout: 60*1000,
// Http请求头
headers: {
"api": "1.0.0",
}
));
// 请求与响应拦截器
_dio.interceptors.add(OnReqResInterceptors());
// 异常拦截器
_dio.interceptors.add(OnErrorInterceptors());
}
}
/// get请求
Future get({@required String url, Map params}) async {
return await requestHttp(url, method: DioMethod.get, params: params);
}
/// post 请求
Future post({@required String url, Map params}) async {
return await requestHttp(url, method: DioMethod.post, params: params);
}
/// put 请求
Future put({@required String url, Map params}) async {
return await requestHttp(url, method: DioMethod.put, params: params);
}
/// delete 请求
Future delete({@required String url, Map params}) async {
return await requestHttp(url, method: DioMethod.delete, params: params);
}
/// Dio request 方法
Future requestHttp(String url, {DioMethod method = DioMethod.get, Map params}) async {
const methodValues = {
DioMethod.get: 'get',
DioMethod.post: 'post',
DioMethod.delete: 'delete',
DioMethod.put: 'put'
};
// 添加 token
TokenModel tokenModel = await SpUtil().loadToken();
if (tokenModel.userToken != null) {
_dio.options.headers = {'Authorization': 'Bearer ' + tokenModel.userToken};
}
try {
Response response;
// 不同请求方法,不同的请求参数。按实际项目需求分,这里 get 是 queryParameters,其它用 data. FormData 也是 data
// 注意: 只有 post 方法支持发送 FormData.
switch (method) {
case DioMethod.get:
response = await _dio.request(url, queryParameters: params, options: Options(method: methodValues[method]));
break;
default:
response = await _dio.request(url, data: params, options: Options(method: methodValues[method]));
}
// JSON 序列化, Response<dynamic> 转 Map<String, dynamic>
String jsonStr = json.encode(response.data);
Map<String, dynamic> map = json.decode(jsonStr);
// PS: 取得 json 数据后, 只返回需要的数据,如果数据没有在外面包一层 BaseModel, 直接返回就可以。
var baseModel = BaseModel.fromJson(map);
return baseModel.data;
} on DioError catch (e) {
// throw e;
// print('DioError--- ${e.type}');
// 出现错误都返回空,错误在 OnErrorInterceptors 类处理。
return null;
}
}
}
OnReqResInterceptors 请求与响应拦截器
/// Dio 请求与响应拦截器
class OnReqResInterceptors extends InterceptorsWrapper {
/// 请求拦截
@override
Future onRequest(RequestOptions options) {
if (Global.isDebug) {
print("请求baseUrl:${options.baseUrl}");
print("请求url:${options.path}");
print('请求头: ${options.headers.toString()}');
if (options.data != null) {
print('请求参数: ${options.data.toString()}');
}
}
return super.onRequest(options);
}
/// 响应拦截
@override
Future onResponse(Response response) {
Response res = response;
if (response != null) {
if (Global.isDebug) {
print('响应: ${response.toString()}');
}
}
if (response.statusCode == 200) {
int errCode = response.data["errCode"];
if (errCode == ErrCode.SUCCESS) {
res = response;
}
}
return super.onResponse(res);
}
}
OnErrorInterceptors 拦截器
/// Dio OnError 拦截器
class OnErrorInterceptors extends InterceptorsWrapper {
/// 异常拦截
@override
Future onError(DioError err) {
if (Global.isDebug) {
print('请求异常: ${err.toString()}');
print('请求异常信息: ${err.response?.toString() ?? ""}');
}
// 异常分类
switch (err.type) {
// 4xx 5xx response
case DioErrorType.RESPONSE:
// JSON 序列化, Response<dynamic> 转 Map<String, dynamic>
String jsonStr = json.encode(err.response.data);
Map<String, dynamic> map = json.decode(jsonStr);
BaseModel baseModel = BaseModel.fromJson(map);
// 处理自定义错误
switch (baseModel.errCode) {
case ErrCode.SUCCESS:
print('0 在这里是不可能出现的,出现的就是有错');
break;
case ErrCode.TOKEN_ERR:
MLToast.error('未登陆');
// 跳转到登录页
Routes.toWelcome();
break;
case ErrCode.DEVICE_TIMEOUT:
MLToast.error('设备响应超时');
break;
default:
print('DioError default');
// 错误提示
MLToast.error(baseModel.message);
}
break;
case DioErrorType.CONNECT_TIMEOUT:
MLToast.error('连接超时');
break;
case DioErrorType.SEND_TIMEOUT:
MLToast.error('发送超时');
break;
case DioErrorType.RECEIVE_TIMEOUT:
MLToast.error('接收超时');
break;
case DioErrorType.CANCEL:
MLToast.error('取消连接');
break;
case DioErrorType.DEFAULT:
MLToast.error('连接异常');
break;
}
return super.onError(err);
}
}
API 地址类
class Api {
/// 基类
static const baseUrl = "https://www.xxx.com/api/v1";
/// 主页
static const homeUrl = "/home";
}
ErrCode 错误编码类
/// 错误编码
class ErrCode {
/// 成功
static const SUCCESS = 0;
/// 权限错误
static const TOKEN_ERR = 20401;
}
SpUtil
另外,这里封装了 shared_preferences 工具类 来保存 Token, 这个按自己实际需求做就行,不用引入。
import 'package:flutter/cupertino.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:smarthome/models/token.dart';
/// shared_preferences 工具类-保存与读取数据
class SpUtil {
/// Token
static const exp = 'token_exp';
static const id = 'token_id';
static const name = 'token_name';
static const haveGateway = 'token_haveGateway';
static const userToken = 'token_user';
static const deviceNumber = 'token_deviceNumber';
/// 保存 Token
saveToken({@required TokenModel tokenModel}) async {
SharedPreferences sp = await SharedPreferences.getInstance();
sp.setInt(exp, tokenModel.exp);
sp.setInt(id, tokenModel.id);
sp.setString(name, tokenModel.name);
sp.setInt(haveGateway, tokenModel.haveGateway);
sp.setString(userToken, tokenModel.userToken);
sp.setInt(deviceNumber, tokenModel.deviceNumber);
}
/// 加载 Token
Future<TokenModel> loadToken() async {
SharedPreferences sp = await SharedPreferences.getInstance();
TokenModel tokenModel = TokenModel();
tokenModel.exp = sp.getInt(exp);
tokenModel.id = sp.getInt(id);
tokenModel.name = sp.getString(name);
tokenModel.haveGateway = sp.getInt(haveGateway);
tokenModel.userToken = sp.getString(userToken);
tokenModel.deviceNumber = sp.getInt(deviceNumber);
return tokenModel;
}
/// 移除 Token
removeToken() async {
SharedPreferences sp = await SharedPreferences.getInstance();
sp.remove(exp);
sp.remove(id);
sp.remove(name);
sp.remove(haveGateway);
sp.remove(userToken);
sp.remove(deviceNumber);
}
}
BaseModel 基类
class BaseModel {
String message;
int errCode;
dynamic data;
BaseModel({this.message, this.errCode, this.data});
BaseModel.fromJson(Map<String, dynamic> json) {
message = json['message'];
errCode = json['errCode'];
data = json['data'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['message'] = this.message;
data['errCode'] = this.errCode;
data['data'] = this.data;
return data;
}
}
DioManager 使用方法
Get 请求
/// 获取所有场景
static Future<List<SceneModel>> getScenes() async {
var json = await DioManager().get(url: Api.scenesUrl);
// 请求数据出错
if (json == null) {
return null;
}
List jsonArray = json;
var sceneModels = List<SceneModel>();
jsonArray.forEach((element) {
sceneModels.add(SceneModel.fromJson(element));
});
return sceneModels;
}
Post 请求
/// 保存场景
static Future saveScene({@required Map params}) async {
var json = await DioManager().post(url: Api.scenesUrl, params: params);
// 请求数据出错
if (json == null) {
return null;
}
print(json);
return '保存成功';
}
Put 请求
/// 更新场景
static Future updataScene({@required Map params}) async {
var json = await DioManager().put(url: Api.scenesUrl, params: params);
// 请求数据出错
if (json == null) {
return null;
}
print(json);
return '更新成功';
}
Delete 请求
/// 删除场景
static Future removeScene({@required int sceneId}) async {
var params = {"sceneId": sceneId};
var json = await DioManager().delete(url: Api.scenesUrl, params: params);
// 请求数据出错
if (json == null) {
return null;
}
return '删除成功';
}
后记: 网络工具类基本封装完成,这里请求返回的数据类型都是 JSON 序列化, 然后转成基础模型,如果返回的数据是 String 类,就那么就用做转化就行。
- 原创文章,转发请注明,谢谢~
转载自:https://juejin.cn/post/6875185393810014221