Flutter 网络工具类封装

关键字 :FlutterAPP
# Flutter 网络工具类封装

*前言: Flutter 的网络请求,可以使用 Dart 原生网络请求 HttpClient,但是这样来就很多功能需要我们重新做一次轮子。所以一般来说,可以找个知名的第三方开源框架做封装使用。比如 Dio...*

## Flutter 引入 Dio 库

*dio 是一个强大的 Dart Http请求库,支持 Restful API、FormData、拦截器、请求取消、Cookie 管理、文件上传/下载、超时、自定义适配器等...*

> 打开 https://pub.dev/packages/dio 找到最新版本。

### 添加依赖

```dart
dependencies:
dio: ^3.x.x // 请使用 pub 上 3.0.0 分支的最新版本
```

### 极简的示例

```dart
import 'package:dio/dio.dart';
void getHttp() async {
try {
Response response = awaitDio().get("http://www.baidu.com");
print(response);
} catch (e) {
print(e);
}
}
```

关于 Dio 详细的使用说明,可以看说明文档: https://github.com/flutterchina/dio/blob/master/README-ZH.md

## Dio 封装

#### 为什么要封装

1. 可以做一些公共处理,方便灵活使用。
2. 方便以后换成别的第三方开源框架。

## DioManager

单例网络工具类

```dart
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 {
// 单例
factoryDioManager() =>_getInstance();
staticDioManager _instance;
staticDioManager_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请求
Futureget({@requiredString url, Map params}) async {
returnawaitrequestHttp(url, method: DioMethod.get, params: params);
}

/// post 请求
Futurepost({@requiredString url, Map params}) async {
returnawaitrequestHttp(url, method: DioMethod.post, params: params);
}

/// put 请求
Futureput({@requiredString url, Map params}) async {
returnawaitrequestHttp(url, method: DioMethod.put, params: params);
}

/// delete 请求
Futuredelete({@requiredString url, Map params}) async {
returnawaitrequestHttp(url, method: DioMethod.delete, params: params);
}

/// Dio request 方法
FuturerequestHttp(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 = awaitSpUtil().loadToken();
if (tokenModel.userToken != null) {
_dio.options.headers = {'Authorization': 'Bearer ' + tokenModel.userToken};
}

try {
Response response;
// 不同请求方法,不同的请求参数。按实际项目需求分,这里 get 是 queryParameters,其它用 data. FormData 也是 data
// 注意: 只有 post 方法支持发送 FormData.
switch (method) {
caseDioMethod.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 转 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 类处理。
returnnull;
}
}
}​
```



### OnReqResInterceptors 请求与响应拦截器

```dart
/// Dio 请求与响应拦截器
class OnReqResInterceptors extends InterceptorsWrapper {
/// 请求拦截
@override
FutureonRequest(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()}');
}
}
returnsuper.onRequest(options);
}

/// 响应拦截
@override
FutureonResponse(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;
}
}
returnsuper.onResponse(res);
}
}​
```

### OnErrorInterceptors 拦截器

```dart
/// Dio OnError 拦截器
class OnErrorInterceptors extends InterceptorsWrapper {
/// 异常拦截
@override
FutureonError(DioError err) {
if (Global.isDebug) {
print('请求异常: ${err.toString()}');
print('请求异常信息: ${err.response?.toString() ?? ""}');
}
// 异常分类
switch (err.type) {

// 4xx 5xx response
caseDioErrorType.RESPONSE:
// JSON 序列化, Response 转 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) {
caseErrCode.SUCCESS:
print('0 在这里是不可能出现的,出现的就是有错');
break;
caseErrCode.TOKEN_ERR:
MLToast.error('未登陆');
// 跳转到登录页
Routes.toWelcome();
break;
caseErrCode.DEVICE_TIMEOUT:
MLToast.error('设备响应超时');
break;
default:
print('DioError default');
// 错误提示
MLToast.error(baseModel.message);
}
break;
caseDioErrorType.CONNECT_TIMEOUT:
MLToast.error('连接超时');
break;
caseDioErrorType.SEND_TIMEOUT:
MLToast.error('发送超时');
break;
caseDioErrorType.RECEIVE_TIMEOUT:
MLToast.error('接收超时');
break;
caseDioErrorType.CANCEL:
MLToast.error('取消连接');
break;
caseDioErrorType.DEFAULT:
MLToast.error('连接异常');
break;
}
returnsuper.onError(err);
}
}​
```

### API 地址类

```dart
class Api {
/// 基类
staticconst baseUrl = "https://www.xxx.com/api/v1";
/// 主页
staticconst homeUrl = "/home";
}​
```

### ErrCode 错误编码类

```dart
/// 错误编码
class ErrCode {

/// 成功
staticconstSUCCESS = 0;
/// 权限错误
staticconstTOKEN_ERR = 20401;
}​
```

### SpUtil

另外,这里封装了 shared_preferences 工具类 来保存 Token, 这个按自己实际需求做就行,不用引入。

```dart
import 'package:flutter/cupertino.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:smarthome/models/token.dart';

/// shared_preferences 工具类-保存与读取数据
class SpUtil {
/// Token
staticconst exp = 'token_exp';
staticconst id = 'token_id';
staticconst name = 'token_name';
staticconst haveGateway = 'token_haveGateway';
staticconst userToken = 'token_user';
staticconst deviceNumber = 'token_deviceNumber';

/// 保存 Token
saveToken({@requiredTokenModel tokenModel}) async {
SharedPreferences sp = awaitSharedPreferences.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 loadToken() async {
SharedPreferences sp = awaitSharedPreferences.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 = awaitSharedPreferences.getInstance();
sp.remove(exp);
sp.remove(id);
sp.remove(name);
sp.remove(haveGateway);
sp.remove(userToken);
sp.remove(deviceNumber);
}
}​
```

### BaseModel 基类

```dart
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() {
finalMap<String, dynamic> data = newMap<String, dynamic>();
data['message'] = this.message;
data['errCode'] = this.errCode;
data['data'] = this.data;
return data;
}
}​
```



## DioManager 使用方法

### Get 请求

```dart
/// 获取所有场景
staticFuture getScenes() async {
var json = awaitDioManager().get(url: Api.scenesUrl);
// 请求数据出错
if (json == null) {
returnnull;
}
List jsonArray = json;
var sceneModels = List();
jsonArray.forEach((element) {
sceneModels.add(SceneModel.fromJson(element));
});

return sceneModels;
}​
```

### Post 请求

```dart
/// 保存场景
staticFuturesaveScene({@requiredMap params}) async {
var json = awaitDioManager().post(url: Api.scenesUrl, params: params);
// 请求数据出错
if (json == null) {
returnnull;
}
print(json);
return'保存成功';
}​
```

### Put 请求

```dart
/// 更新场景
staticFutureupdataScene({@requiredMap params}) async {
var json = awaitDioManager().put(url: Api.scenesUrl, params: params);
// 请求数据出错
if (json == null) {
returnnull;
}
print(json);
return'更新成功';
}​
```

### Delete 请求

```dart
/// 删除场景
staticFutureremoveScene({@requiredint sceneId}) async {
var params = {"sceneId": sceneId};
var json = awaitDioManager().delete(url: Api.scenesUrl, params: params);
// 请求数据出错
if (json == null) {
returnnull;
}
return'删除成功';
}​
```

> 后记: 网络工具类基本封装完成,这里请求返回的数据类型都是 JSON 序列化, 然后转成基础模型,如果返回的数据是 String 类,就那么就用做转化就行。

------

- 原创文章,转发请注明,谢谢~

★博文内容均由个人提供,与平台无关,如有违法或侵权,请与网站管理员联系。

★文明上网,请理性发言。内容一周内被举报5次,发文人进小黑屋喔~

评论