Flutter 基于 Dio 封装的一个 HTTP 请求工具类(XHttp),使用单例方便全局请求管理与使用。 包括请求拦截、响应拦截、错误拦截、请求 Hooks、日志输出、取消请求、取消重复请求、设置白名单、权限管控、基础请求等等功能。
话不多说,直接上代码,欢迎大佬指导。(此处工具类我全部写在一起哦,当然也可以自己手动分开。)
// ignore_for_file: unnecessary_this
import 'dart:convert';
import 'package:dio/dio.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter_easyloading/flutter_easyloading.dart';
/// 全局网络请求 dio 实例 单例 XHttp
class XHttp {
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 const CUSTOM_ERROR_CODE = 'DIO_CUSTOM_ERROR'; // 自定义错误代码
static const REQUEST_TYPE_STR = 'REQUEST'; // 请求类型字符串
static const RESPONSE_TYPE_STR = 'RESPONSE'; // 响应类型字符串
static const ERROR_TYPE_STR = 'RESPONSE_ERROR'; // 错误类型字符串
static const DEFAULT_LOAD_MSG = '请求中...'; // 默认请求提示文字
static const CONNECT_TIMEOUT = 60000; // 连接超时时间
static const RECEIVE_TIMEOUT = 60000; // 接收超时时间
static const SEND_TIMEOUT = 60000; // 发送超时时间
static const DIALOG_TYPE_OTHERS = 'OTHERS'; // 结果处理-其他类型
static const DIALOG_TYPE_TOAST = 'TOAST'; // 结果处理-轻提示类型
static const DIALOG_TYPE_ALERT = 'ALERT'; // 结果处理-弹窗类型
static const DIALOG_TYPE_CUSTOM = 'CUSTOM'; // 结果处理-自定义处理
static String loadMsg = DEFAULT_LOAD_MSG; // 请求提示文字
static String errorShowTitle = '发生错误啦'; // 错误提示标题
static String errorShowMsg; // 错误提示文字
static CancelToken cancelToken = CancelToken(); // 取消网络请求 token,默认所有请求都可取消。
static CancelToken whiteListCancelToken = CancelToken(); // 取消网络请求白名单 token,此 token 不会被取消。
Map<String, CancelToken> _pendingRequests = {}; // 正在请求列表
static Dio dio;
String _getBaseUrl() => 'https://xxx.com';
/// 通用全局单例,第一次使用时初始化。
XHttp._internal() {
if (null == dio) {
dio = Dio(BaseOptions(
baseUrl: _getBaseUrl(),
// contentType: ,
// responseType: ,
headers: {'Content-Type': 'application/json'},
connectTimeout: CONNECT_TIMEOUT,
receiveTimeout: RECEIVE_TIMEOUT,
sendTimeout: SEND_TIMEOUT,
extra: {'cancelDuplicatedRequest': true}, // 是否取消重复请求
));
_init();
}
}
/// 获取单例本身
static final XHttp _instance = XHttp._internal();
/// 取消重复的请求
void _removePendingRequest(String tokenKey) {
if (_pendingRequests.containsKey(tokenKey)) {
// 如果在 pending 中存在当前请求标识,需要取消当前请求,并且移除。
_pendingRequests[tokenKey]?.cancel(tokenKey);
_pendingRequests.remove(tokenKey);
}
}
/// 初始化 dio
void _init() {
// 添加拦截器
dio.interceptors.add(
InterceptorsWrapper(
onRequest: (RequestOptions options, handler) async {
if (kDebugMode) {
print("请求之前");
}
if (dio.options.extra['cancelDuplicatedRequest'] == true && options.cancelToken == null) {
String tokenKey = [
options.method,
options.baseUrl + options.path,
jsonEncode(options.data ?? {}),
jsonEncode(options.queryParameters ?? {})
].join('&');
_removePendingRequest(tokenKey);
options.cancelToken = CancelToken();
options.extra['tokenKey'] = tokenKey;
_pendingRequests[tokenKey] = options.cancelToken;
}
_handleRequest(options, handler);
// 有 token 时,添加 token。放打印日志后面,避免泄露 token。
// 也可以登录成功后掉用 XHttp.setToken() 方法设置 token,但是持久化的话还是要这样最好。
String token = 'Bearer xxxxx';
if (token != dio.options.headers['authorization']) {
dio.options.headers['authorization'] = token;
options.headers['authorization'] = token; // 不设置的话第一次的请求会有问题,上面的是全局设置尚未对本条请求生效。
}
return handler.next(options);
},
onResponse: (Response response, ResponseInterceptorHandler handler) {
if (kDebugMode) {
print("响应之前");
}
_handleResponse(response, handler);
RequestOptions option = response.requestOptions;
if (dio.options.extra['cancelDuplicatedRequest'] == true && option.cancelToken == null) {
_removePendingRequest(option.extra['tokenKey']);
}
String code = (response?.data ?? {})['code'];
String msg = (response?.data ?? {})['msg'] ?? response.statusMessage;
// 静态数据 或者 根据后台实际返回结构解析,即 code == '0' 时,data 为有效数据。
bool isSuccess = option.contentType != null && option.contentType.contains("text") || code == '0';
response.data = Result(response.data, isSuccess, response.statusCode, msg, headers: response.headers);
return handler.next(response);
},
onError: (DioError error, handler) {
if (kDebugMode) {
print("出错之前");
}
_handleError(error);
if (!CancelToken.isCancel(error) && dio.options.extra['cancelDuplicatedRequest'] == true) {
_pendingRequests.clear(); // 不可抗力错误则清空列表
}
// 发生错误同时也会返回一个 Result 结构,通过这个 Result 可以拿到响应状态等信息。
if (error.response != null && error.response?.data != null) {
error.response.data = Result(
error.response.data, false, error.response?.statusCode, errorShowMsg ?? error.response?.statusMessage,
headers: error.response?.headers);
} else {
throw Exception(errorShowMsg);
}
return handler.next(error);
},
),
);
// print("初始化 Dio 完成\n请求超时限制:$CONNECT_TIMEOUT ms\n接收超时限制:$RECEIVE_TIMEOUT ms\n发送超时限制:$SEND_TIMEOUT ms\nDio-BaseUrl:${dio.options.baseUrl}\nDio-Headers:${dio.options.headers}");
}
/// 请求 request 之前统一处理
void _handleRequest(RequestOptions options, handler) {
Toast.hide();
Toast.loading(loadMsg);
Map logData = {
'url': options.baseUrl + options.path,
'method': options.method,
'headers': options.headers,
'data': options.data ?? options.queryParameters, // GET 请求参数可以在 url 中,也可以使用 queryParameters,所以需要增加此判断。
};
_dealRequestInfo(logData, REQUEST_TYPE_STR);
}
/// 响应 response 之前统一处理
void _handleResponse(Response response, handler) {
Map logData = {
'url': response.requestOptions.uri,
'method': response.requestOptions.method,
'headers': response.headers,
'data': response.data,
'statusCode': response.statusCode,
'statusMessage': response.statusMessage,
};
_dealRequestInfo(logData, RESPONSE_TYPE_STR);
Toast.hide();
}
/// 错误 error 统一处理
void _handleError(DioError error) {
// 也可以在此处根据状态码并处理错误信息,例如退出登录等等。
String errorTypeInfo = '其他错误!';
switch (error.type) {
case DioErrorType.connectTimeout:
errorTypeInfo = '连接超时!';
break;
case DioErrorType.sendTimeout:
errorTypeInfo = "请求超时!";
break;
case DioErrorType.receiveTimeout:
errorTypeInfo = "响应超时!";
break;
case DioErrorType.response:
errorTypeInfo = "服务异常!";
break;
case DioErrorType.cancel:
errorTypeInfo = "请求取消!";
break;
case DioErrorType.other:
default:
break;
}
Map logData = {
'url': error.requestOptions.baseUrl + error.requestOptions.path,
'method': error.requestOptions.method,
'headers': error.response?.headers,
'data': error.response?.data,
'statusCode': error.response?.statusCode,
'statusMessage': error.response?.statusMessage,
'errorType': error.type,
'errorMessage': error.message,
'errorTypeInfo': errorTypeInfo,
};
_dealRequestInfo(logData, ERROR_TYPE_STR);
Toast.hide();
errorShowMsg =
"$errorShowTitle ${error.response?.statusCode ?? 'unknown'} $errorTypeInfo \n ${error.response?.statusMessage ?? ''} ${error.message ?? ''} \n ${error.response?.data ?? ''}";
}
/// 合并打印请求日志 REQUEST RESPONSE RESPONSE_ERROR
String _dealRequestInfo(Map logData, String logType) {
String logStr = "\n";
logStr += "========================= $logType START =========================\n";
logStr += "- URL: ${logData['url']} \n";
logStr += "- METHOD: ${logData['method']} \n";
// logStr += "- HEADER: \n { \n";
// logStr += parseData(logData['headers']);
// logStr += "\n } \n";
if (logData['data'] != null) {
logStr += "- ${logType}_BODY: \n";
logStr += "!!!!!----------*!*##~##~##~##*!*##~##~##~##*!*----------!!!!! \n";
logStr += "${parseData(logData['data'])} \n";
logStr += "!!!!!----------*!*##~##~##~##*!*##~##~##~##*!*----------!!!!! \n";
}
if (logType.contains(RESPONSE_TYPE_STR)) {
logStr += "- STATUS_CODE: ${logData['statusCode']} \n";
logStr += "- STATUS_MSG: ${logData['statusMessage']} \n";
}
if (logType == ERROR_TYPE_STR) {
logStr += "- ERROR_TYPE: ${logData['errorType']} \n";
logStr += "- ERROR_MSG: ${logData['errorMessage']} \n";
logStr += "- ERROR_TYPE_INFO: ${logData['errorTypeInfo']} \n";
}
logStr += "========================= $logType E N D =========================\n";
logWrapped(logStr);
return logStr;
}
/// 统一结果提示处理
Future _showResultDialog(Response response, resultDialogConfig) async {
if (response == null) {
return;
}
resultDialogConfig = resultDialogConfig ?? {};
String dialogType = resultDialogConfig['type'] ?? XHttp.DIALOG_TYPE_TOAST;
if (dialogType == XHttp.DIALOG_TYPE_OTHERS) {
return; // 其他类型 OTHERS 自定义处理
}
bool isSuccess = response?.data?.success ?? false;
String msg = response?.data?.msg ?? '未知错误';
if (dialogType == XHttp.DIALOG_TYPE_TOAST) {
// resultDialogConfig 可以有 successMsg, errorMsg
isSuccess
? Toast.show(resultDialogConfig['successMsg'] ?? msg, type: Toast.SUCCESS)
: Toast.show(resultDialogConfig['errorMsg'] ?? msg, type: Toast.ERROR);
return;
}
if (dialogType == XHttp.DIALOG_TYPE_ALERT) {
// resultDialogConfig 可以有 title, content, closeable, showCancel, cancelText, confirmText, confirmCallback, cancelCallback, closeCallback ...
// Utils.showDialog(...);
return;
}
if (dialogType == XHttp.DIALOG_TYPE_CUSTOM) {
// resultDialogConfig 可以有 onSuceess, onError
if (isSuccess) {
if (resultDialogConfig['onSuccess'] != null) {
resultDialogConfig['onSuccess'](response.data);
}
} else {
if (resultDialogConfig['onError'] != null) {
resultDialogConfig['onError'](response.data);
}
}
}
}
/// 处理异常
void _catchOthersError(e) {
String errMsg = "${errorShowMsg ?? e}$CUSTOM_ERROR_CODE".split(CUSTOM_ERROR_CODE)[0];
int errMsgLength = errMsg.length;
String errshowMsg = errMsgLength > 300 ? errMsg.substring(0, 150) : errMsg;
if (e is DioError) {
if (CancelToken.isCancel(e)) {
Toast.show('Cancel Request Successful'); // 取消重复请求可能会多次弹窗
return;
}
Toast.show(errshowMsg, type: Toast.WARNING);
return;
}
Toast.show(errshowMsg + "\n......", type: Toast.ERROR);
}
/// 本可以直接 XHttp.xxx 调用(添加 static 关键字给之后的 get/post 等方法),但是考虑多台服务器的情况,建议 XHttp.getInstance().xxx 调用。
static XHttp getInstance({String baseUrl, String msg}) {
String targetBaseUrl = baseUrl ?? _instance._getBaseUrl();
loadMsg = msg ?? DEFAULT_LOAD_MSG;
if (dio.options.baseUrl != targetBaseUrl) {
dio.options.baseUrl = targetBaseUrl;
}
return _instance;
}
/// 取消普通请求
static XHttp cancelRequest() {
Toast.hide();
if (dio.options.extra['cancelDuplicatedRequest'] == true) {
_instance._pendingRequests.forEach((tokenKey, cancelToken) {
cancelToken.cancel('cancel request $tokenKey');
});
} else {
cancelToken.cancel('cancel request');
cancelToken = CancelToken(); // 坑!取消后必须重新创建 cancelToken 否则后面使用原来 cancelToken 的请求会无效
}
return _instance;
}
/// 取消所有白名单 cancelToken 的请求
static XHttp cancelWhiteListRequest() {
Toast.hide();
whiteListCancelToken.cancel('cancel whiteList request');
whiteListCancelToken = CancelToken();
return _instance;
}
/// 获取 cancelToken
static CancelToken getCancelToken() {
return cancelToken;
}
/// 获取 whiteListCancelToken
static CancelToken getWhiteListCancelToken() {
return whiteListCancelToken;
}
/// 获取一个新的 cancelToken
static CancelToken getNewCancelToken() {
return CancelToken();
}
/// get 请求
Future get(String url, [Map<String, dynamic> params, resultDialogConfig, bool isCancelWhiteList = false]) async {
// 可转为使用 request 代替,简化代码。
// 写中括号可以忽略参数名称,因为必须按顺序传参。
Response response;
var requestToken;
if (dio.options.extra['cancelDuplicatedRequest'] != true || isCancelWhiteList) {
if (isCancelWhiteList) {
requestToken = whiteListCancelToken;
} else {
requestToken = cancelToken;
}
}
try {
if (params != null) {
response = await dio.get(url, queryParameters: params, cancelToken: requestToken);
return response.data;
} else {
response = await dio.get(url, cancelToken: requestToken);
return response.data;
}
} catch (e) {
_catchOthersError(e);
} finally {
_showResultDialog(response, resultDialogConfig);
}
}
/// post 请求
Future post(String url, [Map<String, dynamic> data, resultDialogConfig, bool isCancelWhiteList = false]) async {
// 可转为使用 request 代替,简化代码。
Response response;
var requestToken;
if (dio.options.extra['cancelDuplicatedRequest'] != true || isCancelWhiteList) {
if (isCancelWhiteList) {
requestToken = whiteListCancelToken;
} else {
requestToken = cancelToken;
}
}
try {
response = await dio.post(url, data: data, cancelToken: requestToken);
return response.data;
} catch (e) {
_catchOthersError(e);
} finally {
_showResultDialog(response, resultDialogConfig);
}
}
/// put 请求
Future put(String url, [Map<String, dynamic> data, resultDialogConfig, bool isCancelWhiteList = false]) async {
// 可转为使用 request 代替,简化代码。
Response response;
var requestToken;
if (dio.options.extra['cancelDuplicatedRequest'] != true || isCancelWhiteList) {
if (isCancelWhiteList) {
requestToken = whiteListCancelToken;
} else {
requestToken = cancelToken;
}
}
try {
response = await dio.put(url, data: data, cancelToken: requestToken);
return response.data;
} catch (e) {
_catchOthersError(e);
} finally {
_showResultDialog(response, resultDialogConfig);
}
}
/// patch 请求
Future patch(String url, [Map<String, dynamic> data, resultDialogConfig, bool isCancelWhiteList = false]) async {
// 可转为使用 request 代替,简化代码。
Response response;
var requestToken;
if (dio.options.extra['cancelDuplicatedRequest'] != true || isCancelWhiteList) {
if (isCancelWhiteList) {
requestToken = whiteListCancelToken;
} else {
requestToken = cancelToken;
}
}
try {
response = await dio.patch(url, data: data, cancelToken: requestToken);
return response.data;
} catch (e) {
_catchOthersError(e);
} finally {
_showResultDialog(response, resultDialogConfig);
}
}
/// delete 请求
Future delete(String url, [Map<String, dynamic> data, resultDialogConfig, bool isCancelWhiteList = false]) async {
// 可转为使用 request 代替,简化代码。
Response response;
var requestToken;
if (dio.options.extra['cancelDuplicatedRequest'] != true || isCancelWhiteList) {
if (isCancelWhiteList) {
requestToken = whiteListCancelToken;
} else {
requestToken = cancelToken;
}
}
try {
response = await dio.delete(url, data: data, cancelToken: requestToken);
return response.data;
} catch (e) {
_catchOthersError(e);
} finally {
_showResultDialog(response, resultDialogConfig);
}
}
/// request
static Future request(
String url, {
String method = XHttp.GET,
Map<String, dynamic> queryParameters,
Map<String, dynamic> data,
bool isCancelWhiteList = false,
resultDialogConfig,
Options options,
void Function(int, int) onSendProgress,
void Function(int, int) onReceiveProgress,
String msg,
String baseUrl,
}) async {
XHttp.getInstance(baseUrl: baseUrl, msg: msg);
Response response;
var requestToken;
if (dio.options.extra['cancelDuplicatedRequest'] != true || isCancelWhiteList) {
if (isCancelWhiteList) {
requestToken = whiteListCancelToken;
} else {
requestToken = cancelToken;
}
}
try {
response = await dio.request(
url,
options: options ?? Options(method: method, contentType: Headers.formUrlEncodedContentType),
queryParameters: queryParameters,
data: data,
cancelToken: requestToken,
onReceiveProgress: onReceiveProgress,
onSendProgress: onSendProgress,
);
return response.data;
} catch (e) {
_instance._catchOthersError(e);
} finally {
_instance._showResultDialog(
response,
resultDialogConfig ?? {'type': XHttp.DIALOG_TYPE_OTHERS},
); // request 请求默认都需自己处理结果
}
}
/// 下载文件
Future downloadFile(urlPath, savePath, [resultDialogConfig, bool isCancelWhiteList = false]) async {
Response response;
var requestToken;
if (dio.options.extra['cancelDuplicatedRequest'] != true || isCancelWhiteList) {
if (isCancelWhiteList) {
requestToken = whiteListCancelToken;
} else {
requestToken = cancelToken;
}
}
try {
response = await dio.download(urlPath, savePath, onReceiveProgress: (int count, int total) {
// 进度
print("$count $total");
}, cancelToken: requestToken);
return response.data;
} catch (e) {
_catchOthersError(e);
} finally {
_showResultDialog(response, resultDialogConfig);
}
}
// /// post 表单请求 【Web】
// Future postForm(String url, [Map<String, dynamic> params, resultDialogConfig, bool isCancelWhiteList = false]) async {
// Response response;
// var requestToken;
// if (dio.options.extra['cancelDuplicatedRequest'] != true || isCancelWhiteList) {
// if (isCancelWhiteList) {
// requestToken = whiteListCancelToken;
// } else {
// requestToken = cancelToken;
// }
// }
// try {
// response = await dio.post(url, queryParameters: params, cancelToken: requestToken);
// return response.data;
// } catch (e) {
// _catchOthersError(e);
// } finally {
// _showResultDialog(response, resultDialogConfig);
// }
// }
/// +++++++++++++++++++++++++ 小扩展 【待增加:retry、代理/proxy、根据状态码自动退出与重连等】 +++++++++++++++++++++++++
/// 获取当前的 baseUrl
static String getBaseUrl() {
return dio.options.baseUrl;
}
/// 设置当前的 baseUrl
static XHttp setBaseUrl(String baseUrl) {
dio.options.baseUrl = baseUrl;
return _instance;
}
/// 获取当前 headers
static Map getHeaders() {
return dio.options.headers;
}
/// 获取当前 headers 属性
static dynamic getHeader(String key) {
return dio.options.headers[key];
}
/// 设置当前 headers
static XHttp setHeaders(Map headers) {
dio.options.headers = headers;
return _instance;
}
/// 设置当前 headers 属性
static XHttp setHeader(String key, String value) {
dio.options.headers[key] = value;
return _instance;
}
/// 删除当前的请求头属性
static XHttp removeHeader(String key) {
dio.options.headers.remove(key);
return _instance;
}
/// 删除当前的所有请求头属性
static XHttp removeAllHeaders() {
dio.options.headers.clear();
return _instance;
}
/// 获取当前的所有超时时间
static Map getRequestTimeout() {
return {
'connectTimeout': dio.options.connectTimeout,
'receiveTimeout': dio.options.receiveTimeout,
'sendTimeout': dio.options.sendTimeout
};
}
/// 设置当前的所有超时时间
static XHttp setRequestTimeout(int timeout) {
dio.options.connectTimeout = timeout;
dio.options.receiveTimeout = timeout;
dio.options.sendTimeout = timeout;
return _instance;
}
/// 设置当前的连接超时时间
static XHttp setConnectTimeout(int timeout) {
dio.options.connectTimeout = timeout;
return _instance;
}
/// 设置当前的接收超时时间
static XHttp setReceiveTimeout(int timeout) {
dio.options.receiveTimeout = timeout;
return _instance;
}
/// 设置当前的发送超时时间
static XHttp setSendTimeout(int timeout) {
dio.options.sendTimeout = timeout;
return _instance;
}
/// 获取用户数据
static Map<String, dynamic> getAuthUser() {
String token = dio.options.headers['authorization'];
if (null == token) {
return null;
}
// 解析token
return {'account': 'xxx', 'name': 'xxx', 'roles': 'xxx'};
}
/// 设置当前 token
static XHttp setAuthToken([String token]) {
if (null == token) {
dio.options.headers.remove('authorization');
} else {
dio.options.headers['authorization'] = token;
}
return _instance;
}
/// 设置错误提示标题
static XHttp setErrorTitle(String msg) {
errorShowTitle = msg;
return _instance;
}
/// 判断是否是取消异常
static bool isCancel(e) {
return CancelToken.isCancel(e);
}
// /// 设置当前的请求数据格式
// static XHttp setContentType(String contentType) {
// dio.options.contentType = contentType;
// return _instance;
// }
// /// 设置当前的请求数据格式
// static XHttp setContentTypeMultipartForm() {
// dio.options.contentType = "multipart/form-data";
// return _instance;
// }
// /// 设置当前的请求返回数据格式
// static XHttp setDataType(ResponseType dataType) {
// dio.options.responseType = dataType;
// return _instance;
// }
// /// 设置当前的请求返回数据格式
// static XHttp setDataTypeJson() {
// dio.options.responseType = ResponseType.json;
// return _instance;
// }
// ----- [cookie/charset/accept/encoder/decoder] 这些都可以通过设置 headers 实现 -----
}
/// ====================================================== 以下内容为工具方法 ======================================================
/// 解析数据
String parseData(data) {
String responseStr = "";
if (data is Map) {
responseStr += data.mapToStructureString();
} else if (data is FormData) {
final formDataMap = Map()
..addEntries(data.fields)
..addEntries(data.files);
responseStr += formDataMap.mapToStructureString();
} else if (data is List) {
responseStr += data.listToStructureString();
} else {
responseStr += data.toString();
}
return responseStr;
}
/// 分段 log,可以写到 log 中。
void logWrapped(String text) {
final pattern = RegExp('.{1,800}'); // 800 is the size of each chunk
pattern.allMatches(text).forEach((match) => print(match.group(0)));
}
/// Map 拓展,Map 转结构化字符串输出。
extension Map2StringEx on Map {
String mapToStructureString({int indentation = 0, String space = " "}) {
if (this == null || this.isEmpty) {
return "$this";
}
String result = "";
String indentationContent = space * indentation;
result += "{";
this.forEach((key, value) {
if (value is Map) {
result += "\n$indentationContent" + "\"$key\": ${value.mapToStructureString(indentation: indentation + 1)},";
} else if (value is List) {
result += "\n$indentationContent" + "\"$key\": ${value.listToStructureString(indentation: indentation + 1)},";
} else {
result += "\n$indentationContent" + "\"$key\": ${value is String ? "\"$value\"," : "$value,"}";
}
});
result = result.substring(0, result.length - 1); // 去掉最后一个逗号
result += "\n$indentationContent}";
return result;
}
}
/// List 拓展,List 转结构化字符串输出。
extension List2StringEx on List {
String listToStructureString({int indentation = 0, String space = " "}) {
if (this == null || this.isEmpty) {
return "$this";
}
String result = "";
String indentationContent = space * indentation;
result += "[";
for (var value in this) {
if (value is Map) {
result +=
"\n$indentationContent" + space + "${value.mapToStructureString(indentation: indentation + 1)},"; // 加空格更好看
} else if (value is List) {
result += value.listToStructureString(indentation: indentation + 1);
} else {
result += "\n$indentationContent" + value is String ? "\"$value\"," : "$value,";
}
}
result = result.substring(0, result.length - 1); // 去掉最后一个逗号
result += "\n$indentationContent]";
return result;
}
}
/// 结果处理
class Result {
var data;
bool success;
int code;
String msg;
var headers;
Result(this.data, this.success, this.code, this.msg, {this.headers});
}
class Toast {
Toast._() {
// EasyLoading 已全局初始化构建
// EasyLoading.instance.loadingStyle = EasyLoadingStyle.custom;
// 此处可自定义风格
}
static final Toast _instance = Toast._();
static const String SUCCESS = "SUCCESS";
static const String ERROR = "ERROR";
static const String WARNING = "WARNING";
static const String INFO = "INFO";
static loading(String msg) {
EasyLoading.show(status: msg);
}
static progeress(double value, String msg) {
EasyLoading.showProgress(value, status: msg);
}
static show(String msg, {String type}) {
switch (type) {
case Toast.SUCCESS:
EasyLoading.showSuccess(msg);
break;
case Toast.ERROR:
EasyLoading.showError(msg);
break;
case Toast.WARNING:
EasyLoading.showInfo(msg);
break;
case Toast.INFO:
default:
EasyLoading.showToast(msg);
break;
}
}
static hide() {
EasyLoading.dismiss();
}
}
// /// 使用示例:若未设置多个 baseUrl,可省略 getInstance(),记得给 get、post 设置 static 关键字或者直接初始化多个 baseUrl 的实例。也可以参考 request 在 get、post 方法中设置 baseUrl。
// XHttp.getInstance().post("/user/login", {
// "username": username,
// "password": password
// }).then((res) {
// // DO SOMETHING
// }).catchError((err) {
// // DO SOMETHING
// });