flutter dio封装结合json_serializable创建网络请求
程序员文章站
2022-06-01 17:53:58
...
1.依赖
dio: ^3.0.9 fluttertoast: ^4.0.1 shared_preferences: ^0.5.7+3 build_runner: ^1.7.1 json_serializable: ^3.2.3
注意:序列化依赖是放在dev_dependencies里面
2.目录结构
3.开始封装dio网络请求
1.创建DioManage
- 利用factory创建dio单列
- 配置dio基本属性options
- 设置拦截器
class DioManager{
static const String BASE_URL = "http://172.16.3.52:8083/V1";
static const int CONNECT_TIMEOUT = 10000;
static const int RECEIVE_TIMEOUT = 10000;
///创建单例
//1.私有静态实例对象
static final DioManager _dioManager = DioManager._instance();
//2.创建工厂方法
factory DioManager() => _dioManager;
//3.私有的命名式构造方法
DioManager._instance() {
if (_dio == null) {
///dio配置基本属性
BaseOptions _options = BaseOptions(
baseUrl: BASE_URL,
contentType: Headers.jsonContentType,
responseType: ResponseType.json,
connectTimeout: CONNECT_TIMEOUT,
receiveTimeout: RECEIVE_TIMEOUT,);
_dio = Dio(_options);
///拦截器
_dio.interceptors
..add(HeadersInterceptors())
..add(LogsInterceptor())
..add(ResponseInterceptors());
}
}
}
2.创建拦截器
-
HeadersInterceptors()头部拦截器,设置token和其他参数
-
LogsInterceptor()日志拦截器,控制台打印数据
-
ResponseInterceptors()相应拦截器,打印接口返回值
上代码
import 'dart:io';
import 'package:dio/dio.dart';
import 'package:shared_preferences/shared_preferences.dart';
class HeadersInterceptors extends InterceptorsWrapper{
var encryptResult = '';
@override
Future onRequest(RequestOptions options) async{
// TODO: implement onRequest
SharedPreferences _sp = await SharedPreferences.getInstance();
options.headers.addAll({
HttpHeaders.contentTypeHeader: 'application/json',
'antappid': 'appId',
'antplatform': Platform.isIOS ? 'ios' : 'android',
'authorization': 'token',
'antciphertext': '加密',
});
return super.onRequest(options);
}
}
根据每个公司不同的头部要求设置相应的头部参数
import 'package:dio/dio.dart';
import 'package:flutter_widget_project/dio_util/http_error.dart';
///日志拦截器
class LogsInterceptor extends InterceptorsWrapper {
@override
Future onError(DioError err) {
print('请求异常信息: ' + HttpError(code: err.type.toString() ,message: err.message).toString());
return super.onError(err);
}
@override
Future onRequest(RequestOptions options) {
print("请求API:${options.baseUrl}${options.path}");
print('请求头: ' + options.headers.toString());
if (options.data != null) {
print('请求参数: ' + options.data.toString());
}
return super.onRequest(options);
}
@override
Future onResponse(Response response) {
return super.onResponse(response);
}
}
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:flutter_widget_project/dio_util/base_model.dart';
import 'package:fluttertoast/fluttertoast.dart';
class ResponseInterceptors extends InterceptorsWrapper {
@override
onResponse(Response response) {
RequestOptions _options = response.request;
String body = response.data.toString();
print("${_options.contentType}");
try{
if(_options.contentType != null && _options.contentType == "text"){
print("HTTP_RESPONSE_BODY::${response.data}");
}
if(response.statusCode == 200){
if(body.length > 600){
for(int i = 0 ; i< body.toString().length ; i+= 600){
if(i+600 < body.toString().length){
print("HTTP_RESPONSE_BODY::${body.toString().substring(i , i+600)}");
}else{
print("HTTP_RESPONSE_BODY::${body.toString().substring(i , body.toString().length)}");
}
}
}else{
print('HTTP_RESPONSE_BODY::$body');
}
}
}catch(e){
print('ERROR::$body');
}
return super.onResponse(response);
}
}
***响应拦截将日志进行字符长度遍历,防止日志过长导致打印信息不全***
3.HttpError错误提示
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:flutter_widget_project/dio_util/constant.dart';
import 'package:flutter_widget_project/dio_util/error_page.dart';
import 'package:fluttertoast/fluttertoast.dart';
class HttpError{
static const int UNAUTHORIZED = 401;
static const int FORBIDDEN = 403;
static const int NOT_FOUND = 404;
static const int REQUEST_TIMEOUT = 408;
static const int INTERNAL_SERVER_ERROR = 500;
static const int BAD_GATEWAY = 502;
static const int SERVICE_UNAVAILABLE = 503;
static const int GATEWAY_TIMEOUT = 504;
///未知错误
static const String UNKNOWN = "UNKNOWN";
///解析错误
static const String PARSE_ERROR = "PARSE_ERROR";
///网络错误
static const String NETWORK_ERROR = "NETWORK_ERROR";
///协议错误
static const String HTTP_ERROR = "HTTP_ERROR";
///证书错误
static const String SSL_ERROR = "SSL_ERROR";
///连接超时
static const String CONNECT_TIMEOUT = "CONNECT_TIMEOUT";
///响应超时
static const String RECEIVE_TIMEOUT = "RECEIVE_TIMEOUT";
///发送超时
static const String SEND_TIMEOUT = "SEND_TIMEOUT";
///网络请求取消
static const String CANCEL = "CANCEL";
String code;
String message;
HttpError({this.code, this.message});
HttpError.dioError(DioError error){
message = error.message;
switch(error.type){
case DioErrorType.CONNECT_TIMEOUT:
code = CONNECT_TIMEOUT;
message = "网络连接超时,请检查网络设置!";
break;
case DioErrorType.RECEIVE_TIMEOUT:
code = RECEIVE_TIMEOUT;
message = "服务器异常,请稍后重试!";
break;
case DioErrorType.SEND_TIMEOUT:
code = SEND_TIMEOUT;
message = "网络连接超时,请检查网络设置!";
break;
case DioErrorType.RESPONSE:
code = HTTP_ERROR;
message = "服务器异常,请稍后重试!";
break;
case DioErrorType.CANCEL:
code = CANCEL;
message = "请求已被取消,请重新请求!";
break;
case DioErrorType.DEFAULT:
code = UNKNOWN;
message = "网络或服务器异常,请稍后重试!";
break;
}
//跳转错误页面
//push(ErrorPage(message: message,));
Fluttertoast.showToast(msg: message);
}
@override
String toString() {
// TODO: implement toString
return 'HttpError{code:$code , message:$message}';
}
}
4.根据返回值结构,创建BaseModel , BaseListModel类
一般结构:data里面的数据才是我们需要的responsbody,所以我们要将拿的的返回值进行封装
{"code":0 ,
"message":"success",
"data":"{}"
}
数据序列化成json
我的数据结构
{
"Code":0,
"Message":"",
"Data":{
"Token":"wewewwwewew",
{
"Id":"112" ,
"Name":"ee"
}
}
}
创建返回值model
// ignore_for_file: non_constant_identifier_names,library_prefixes最顶部这句话可取消警告
part 'user_model.g.dart';创建.g文件不可少
下面这两句是模版
factory LoginModel.fromJson(Map<String, dynamic> json) =>
_$LoginModelFromJson(json);
LoginModel from(Map<String, dynamic> json) => _$LoginModelFromJson(json);
// ignore_for_file: non_constant_identifier_names,library_prefixes
import 'package:flutter_widget_project/dio_util/base_model.dart';
import 'package:json_annotation/json_annotation.dart';
part 'user_model.g.dart';
/*
* 登录接口请求成功的返回值
*
* */
@JsonSerializable(createToJson: false)
class LoginModel{
String Token;
UserModel Member;
LoginModel({this.Token , this.Member});
factory LoginModel.fromJson(Map<String, dynamic> json) =>
_$LoginModelFromJson(json);
LoginModel from(Map<String, dynamic> json) => _$LoginModelFromJson(json);
}
@JsonSerializable(createToJson: false)
class UserModel {
final String Id;
UserModel({
this.Id,
});
factory UserModel.fromJson(Map<String, dynamic> json) =>
_$UserModelFromJson(json);
UserModel from(Map<String, dynamic> json) => _$UserModelFromJson(json);
}
创建完model后在控制台输入flutter packages pub run build_runner build 创建.g文件,如果不成功就使用flutter packages pub run build_runner build --delete-conflicting-outputs再次创建
创建EntityFactory申明model
import 'package:flutter_widget_project/model/entrust_search_model.dart';
import 'package:flutter_widget_project/model/user_model.dart';
class EntityFactory {
static T generateOBJ<T>(json) {
if (json == null) {
return null;
}
//可以在这里加入任何需要并且可以转换的类型,例如下面
// else if (T.toString() == "LoginModel") {
// return LoginModel.fromJson(json) as T;
// }
// else if (T.toString() == "EntrustModel") {
// return EntrustModel.fromJson(json) as T;
// }
// else if (T.toString() == "EntrustListModel") {
// return EntrustListModel.fromJson(json) as T;
// }
else {
return json as T;
}
}
}
BaseModel
import 'entity_factory.dart';
///因为我的接口返回的是大写数据,所以json["Data"].
class BaseModel<T> {
///拿到服务器数据后,我们希望转换成自己方便使用的model结构
int code;
String message;
String serverMessage;
T data;
BaseModel({this.code, this.message, this.data});
factory BaseModel.fromJson(json) {
///获取接口数据
if(json["Data"] != null){
return BaseModel(
code: json["Code"],
message: json["Message"],
// data值需要经过工厂转换为我们传进来的类型
data: EntityFactory.generateOBJ<T>(json["Data"]),
);
}else{
return BaseModel(
code: json["Code"],
message: json["Message"],
data: null
);
}
}
}
BaseListModel
import 'package:flutter_widget_project/dio_util/entity_factory.dart';
import 'package:flutter_widget_project/model/page_option_model.dart';
class BaseListModel<T>{
int code;
String message;
dynamic pageResult;
List<T> data;
BaseListModel({this.code, this.message, this.data ,this.pageResult});
factory BaseListModel.fromJson(json){
List<T> mData = List();
if (json["Data"] != null && json["Data"]["Data"] != null) {
(json["Data"]["Data"] as List).forEach((element) {
mData.add(EntityFactory.generateOBJ<T>(element));
});
}
if(json["Data"] != null){
return BaseListModel(
code: json["Code"],
message: json["Message"],
data: mData,
pageResult: EntityFactory.generateOBJ(json["Data"]["PagingResult"])
);
}else{
return BaseListModel(
code: json["Code"],
message: json["Message"],
data: null,
);
}
}
}
5.再回到DioManager创建请求方法
Future request<T>(
BuildContext context,
NetMethod method,
String path, {
bool isLoading,
Map param,
Map formData,
Function(T) onSuccess,
Function(HttpError) onError,
}) async {
try {
///开始做网络请求
Response response = await _dio.request(path,
queryParameters: param,
data: formData,
options: Options(method: NetMethodValues[method]));
if (response != null) {
///返回数据转化成对应的model实例
BaseModel entity = BaseModel<T>.fromJson(response.data);
///根据状态码处理相应的状态
if (entity.code == 0) {
onSuccess(entity.data);
} else {
onError(HttpError(code: entity.code.toString(), message: entity.message));
}
} else {
onError(HttpError(code: "-1", message: "未知错误"));
}
} on DioError catch (e) {
///异常拦截
onError(HttpError.dioError(e));
}
}
///请求列表
Future requestList<T>(
BuildContext context,
NetMethod method,
String path, {
bool isLoading,
Map param,
Map formData,
Function(List , dynamic) onSuccess,
Function(HttpError) onError,
}) async {
try {
Response response = await _dio.request(path,
queryParameters: param,
data: formData,
options: Options(method: NetMethodValues[method]));
if (response != null) {
BaseListModel entity = BaseListModel<T>.fromJson(response.data);
if (entity.code == 0) {
onSuccess(entity.data , entity.pageResult);
} else {
onError(
HttpError(code: entity.code.toString(), message: entity.message));
}
} else {
onError(HttpError(code: "-1", message: "未知错误"));
}
} on DioError catch (error) {
onError(HttpError.dioError(error));
}
}
}
枚举请求方式
enum NetMethod { GET, POST, DELETE, PUT }
const NetMethodValues = {
NetMethod.GET: "get",
NetMethod.POST: "post",
NetMethod.DELETE: "delete",
NetMethod.PUT: "put"
};
使用
request() {
DioManager().request<LoginModel>(
context,
NetMethod.POST,
"/MemberAuth/Login",
formData: {"Account": "zws1234", "Password": "123456"},
onSuccess: (data) async {
LoginModel dataModel = data;
SharedPreferences sp = await SharedPreferences.getInstance();
sp.setString("accessToken" ,dataModel.Token);
},
onError: (error) {
Fluttertoast.showToast(msg: error.message);
print("error code = ${error.code}, massage = ${error.message}");
},
);
}
分页列表请求
1.先设置监听是否滑动到最底部或者最顶部
import 'package:flutter/material.dart';
import 'package:flutter_widget_project/model/page_option_model.dart';
class PageMini {
ScrollController pageScrollController = new ScrollController();
bool isPageLoading = false;
PageResultModel pageResultModel = new PageResultModel.empty();
PageOptionModel pageOptionModel = new PageOptionModel.empty();
initPage() {
if(pageScrollController != null){
pageScrollController.addListener(() {
if (pageScrollController.position.pixels >=
pageScrollController.position.maxScrollExtent && !isPageLoading) {
isPageLoading = true;
loadMoreData();
}
if (pageScrollController.position.pixels ==
pageScrollController.position.minScrollExtent && !isPageLoading) {
isPageLoading = true;
refreshData();
}
});
}
}
@protected
refreshData() {}
@protected
loadMoreData() {}
}
在页面混入,并设置滚动controller
请求
requestList({int goPage = 1}) {
setState(() {
isLoading = true;
});
goPage = pageOptionModel.GotoPageNumber;
DioManager().requestList<EntrustModel>(
context,
NetMethod.POST,
"/Entrust/Search",
formData: {"EntrustType": 1 , "PagingOption":{"GotoPageNumber":"$goPage" , "PageSize":"15"}},
onSuccess: (response , pageResult){
if(goPage == 1){
dataList.clear();
}
dataList = response;
setState(() {
isLoading = false;
isPageLoading = false;
});
},
onError: (error){
Fluttertoast.showToast(msg: error.message);
setState(() {
isLoading = false;
});
}
);
}
上一篇: 在原生项目中集成Flutter
下一篇: Flutter异常捕获在项目中的应用