Compare commits
2 Commits
1d32b674f7
...
988052630a
Author | SHA1 | Date |
---|---|---|
zhuce | 988052630a | |
zhuce | f5817af759 |
|
@ -0,0 +1,20 @@
|
|||
import 'package:autosos_flutter/util/xhttp.dart';
|
||||
|
||||
class LoginApi {
|
||||
static XHttp xHttp = XHttp.getInstance();
|
||||
|
||||
static void login(int type, String username, String password, String code,
|
||||
String cid, String openid, String unionid) async {
|
||||
var data = {
|
||||
"type": type,
|
||||
"username": username,
|
||||
"password": password,
|
||||
"code": code,
|
||||
"getui_cid": cid,
|
||||
"wx_openid": openid,
|
||||
"wx_unionid": unionid
|
||||
};
|
||||
var post = await xHttp.post("/v2/auth/get-access-token", data);
|
||||
// print(post);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,81 @@
|
|||
class Constant{
|
||||
//测试地址
|
||||
static const String testHost = "http://api.test.jjsos.cn/";
|
||||
//线上地址
|
||||
static const String host = "http://api.jjsos.cn/";
|
||||
//图片的host
|
||||
static const String pictureHost = "http://pic.jjsos.cn/";
|
||||
//工单签字地址
|
||||
static const String testWorkOrderUrl = "http://backend.test.jjsos.cn/old/elc-work-new-app?order_id=";
|
||||
static const String workOrderUrl = "http://backend.jjsos.cn/old/elc-work-new-app?order_id=";
|
||||
//是否在线
|
||||
static bool isOrderStar = false;
|
||||
//是否打开页面
|
||||
static bool isOpen = false;
|
||||
|
||||
//网络是否正常(心跳检测)
|
||||
static bool weberror=false;
|
||||
//上一次心跳时间
|
||||
static int heartbeatTime = 0;
|
||||
static int onlineTime = 0;
|
||||
//是否服务中
|
||||
static bool isService = false;
|
||||
static bool isMain = false;
|
||||
|
||||
//是否有消息
|
||||
static bool hasMessage = false;
|
||||
static bool hasMessageInform = false;
|
||||
|
||||
static const String WX_ID = "wx3bd407849a19c609";
|
||||
static const String WX_Secret = "efb0ca67e9b2a58fb5b460b8b4153563";
|
||||
|
||||
//当前位置
|
||||
static double lat = 0.00;
|
||||
static double lng = 0.00;
|
||||
static String address = "";
|
||||
//退出登录
|
||||
static const String OUT_LOGIN_TOKEN = "OUT_LOGIN_TOKEN";
|
||||
//抛单空驶订单签字回调
|
||||
static const String ORDER_EMPTY_SIGN_TOKEN = "ORDER_EMPTY_SIGN_TOKEN";
|
||||
//新订单来了 推送token
|
||||
static const String ORDER_NEW_TOKEN = "ORDER_NEW_TOKEN";
|
||||
//取消推送
|
||||
static const String ORDER_CANCEL_TOKEN = "ORDER_CANCEL_TOKEN";
|
||||
//空时推送
|
||||
static const String ORDER_EMPTY_TOKEN = "ORDER_EMPTY_TOKEN";
|
||||
//订单刷新通知
|
||||
static const String ORDER_REFRESH_TOKEN = "ORDER_REFRESH_TOKEN";
|
||||
//订单支付完成
|
||||
static const String ORDER_PAY_TOKEN = "ORDER_PAY_TOKEN";
|
||||
//订单完成
|
||||
static const String ORDER_COMPLETE_TOKEN = "ORDER_COMPLETE_TOKEN";
|
||||
//音频token
|
||||
//您有新的内部订单请注意查收
|
||||
static const int NEW_ORDER_INSIDE = 1;
|
||||
//您有新的啾啾救援订单请注意查收
|
||||
static const int NEW_ORDER_JIUJIU = 2;
|
||||
//订单已被后台关闭
|
||||
static const int ORDER_BACKSTAGE_CLOSE = 3;
|
||||
//订单已被修改,请刷新
|
||||
static const int ORDER_BACKSTAGE_MODIFY = 4;
|
||||
//关闭有费空时关闭
|
||||
static const int ORDER_BACKSTAGE_CLOSE_CHARGE = 5;
|
||||
//您是否真的到达救援现场
|
||||
static const int WHETHER_ARRIVE_SCENE = 6;
|
||||
//您已进入离线模式待网络正常后上传照片
|
||||
static const int ENTERED_OFFLINE_MODE = 7;
|
||||
//真的关闭订单吗
|
||||
static const int ORDER_WHETHER_CLOSE = 8;
|
||||
//完成后继续接单
|
||||
static const int ORDER_COMPLETE = 9;
|
||||
//app当前版本号-版本管理
|
||||
static String newVersion="4.1.3.9";
|
||||
|
||||
//更新状态 0=未获取更新信息,1=无需更新,2=需要非强制更新,3=强制更新 4=接口异常(可登录) 5=网络异常(重试)
|
||||
static int NECUPDATE =0;
|
||||
|
||||
static bool isDriverMessage = false;
|
||||
static bool isServiceTrailer = true;
|
||||
static String arriveTipsStr = "";
|
||||
static String uploadStartStr = "";
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
import 'package:autosos_flutter/pages/home/home_page.dart';
|
||||
import 'package:autosos_flutter/pages/login/login_page.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_easyloading/flutter_easyloading.dart';
|
||||
|
||||
void main() {
|
||||
runApp(const MyApp());
|
||||
|
@ -13,11 +14,13 @@ class MyApp extends StatelessWidget {
|
|||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MaterialApp(
|
||||
title: 'Flutter Demo',
|
||||
theme: ThemeData(
|
||||
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
|
||||
useMaterial3: true,
|
||||
),
|
||||
home: const LoginPage());
|
||||
title: 'Flutter Demo',
|
||||
theme: ThemeData(
|
||||
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
|
||||
useMaterial3: true,
|
||||
),
|
||||
home: const LoginPage(),
|
||||
builder: EasyLoading.init(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,10 @@
|
|||
import 'package:autosos_flutter/api/login_api.dart';
|
||||
import 'package:autosos_flutter/util/xhttp.dart';
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
|
||||
|
||||
class LoginPage extends StatefulWidget {
|
||||
const LoginPage({super.key});
|
||||
|
||||
|
@ -8,6 +13,8 @@ class LoginPage extends StatefulWidget {
|
|||
}
|
||||
|
||||
class _LoginPageState extends State<LoginPage> {
|
||||
final TextEditingController _unameController = TextEditingController();
|
||||
final TextEditingController _pwdController = TextEditingController();
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
|
@ -47,39 +54,54 @@ class _LoginPageState extends State<LoginPage> {
|
|||
color: Colors.grey,
|
||||
),
|
||||
),
|
||||
const Expanded(
|
||||
child: TextField(
|
||||
decoration: InputDecoration(hintText: "请输入手机号"),
|
||||
)),
|
||||
],
|
||||
),
|
||||
),
|
||||
const Padding(
|
||||
padding: EdgeInsets.all(20),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: TextField(
|
||||
decoration: InputDecoration(hintText: "请输入密码"),
|
||||
))
|
||||
controller: _unameController,
|
||||
decoration: const InputDecoration(hintText: "请输入手机号"),
|
||||
)),
|
||||
],
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(20),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(12),
|
||||
margin: const EdgeInsets.only(top: 40),
|
||||
width: MediaQuery.of(context).size.width,
|
||||
decoration: BoxDecoration(color: Colors.grey,borderRadius: BorderRadius.circular(21)),
|
||||
child: const Center(
|
||||
child: Text(
|
||||
"登录",
|
||||
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
|
||||
))),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: TextField(
|
||||
controller: _pwdController,
|
||||
decoration: const InputDecoration(hintText: "请输入密码"),
|
||||
))
|
||||
],
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(20),
|
||||
child: GestureDetector(
|
||||
onTap:()=>_login(),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(12),
|
||||
margin: const EdgeInsets.only(top: 40),
|
||||
width: MediaQuery
|
||||
.of(context)
|
||||
.size
|
||||
.width,
|
||||
decoration: BoxDecoration(color: Colors.grey,
|
||||
borderRadius: BorderRadius.circular(21)),
|
||||
child: const Center(
|
||||
child: Text(
|
||||
"登录",
|
||||
style: TextStyle(
|
||||
fontSize: 18, fontWeight: FontWeight.bold),
|
||||
))),)
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _login() async{
|
||||
String username = _unameController.text;
|
||||
String password = _pwdController.text;
|
||||
LoginApi.login(1, username, password, "", "", "", "");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
import 'package:autosos_flutter/util/sp_util.dart';
|
||||
import 'package:basic_utils/basic_utils.dart';
|
||||
import 'package:uuid/uuid.dart';
|
||||
|
||||
class DeviceIdUtils{
|
||||
static String? getDeviceId(){
|
||||
SPUtil spUtil = SPUtil();
|
||||
var deviceId = spUtil.get<String>("JJdeviceId");
|
||||
if(StringUtils.isNullOrEmpty(deviceId)){
|
||||
var uuid = const Uuid();
|
||||
return uuid.v1();
|
||||
}
|
||||
return deviceId;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,85 @@
|
|||
import 'dart:convert';
|
||||
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
class SPUtil{
|
||||
//创建工厂方法
|
||||
static SPUtil? _instance;
|
||||
factory SPUtil() => _instance ??= SPUtil._initial();
|
||||
SharedPreferences? _preferences;
|
||||
//创建命名构造函数
|
||||
SPUtil._initial() {
|
||||
//为什么在这里需要新写init方法 主要是在命名构造中不能使用async/await
|
||||
init();
|
||||
}
|
||||
//初始化SharedPreferences
|
||||
void init() async {
|
||||
_preferences ??= await SharedPreferences.getInstance();
|
||||
}
|
||||
|
||||
//到这里还没有完 有时候会遇到使用时提示 SharedPreferences 未初始化,所以还需要提供一个static 的方法
|
||||
static Future<SPUtil?> perInit() async {
|
||||
if (_instance == null) {
|
||||
//静态方法不能访问非静态变量所以需要创建变量再通过方法赋值回去
|
||||
SharedPreferences preferences = await SharedPreferences.getInstance();
|
||||
_instance = SPUtil._pre(preferences);
|
||||
}
|
||||
return _instance;
|
||||
}
|
||||
SPUtil._pre(SharedPreferences prefs) {
|
||||
_preferences = prefs;
|
||||
}
|
||||
///设置String类型的
|
||||
void setString(key, value) {
|
||||
_preferences?.setString(key, value);
|
||||
}
|
||||
///设置setStringList类型的
|
||||
void setStringList(key, value) {
|
||||
_preferences?.setStringList(key, value);
|
||||
}
|
||||
///设置setBool类型的
|
||||
void setBool(key, value) {
|
||||
_preferences?.setBool(key, value);
|
||||
}
|
||||
///设置setDouble类型的
|
||||
void setDouble(key, value) {
|
||||
_preferences?.setDouble(key, value);
|
||||
}
|
||||
///设置setInt类型的
|
||||
void setInt(key, value) {
|
||||
_preferences?.setInt(key, value);
|
||||
}
|
||||
///存储Json类型的
|
||||
void setJson(key, value) {
|
||||
value = jsonEncode(value);
|
||||
_preferences?.setString(key, value);
|
||||
}
|
||||
///通过泛型来获取数据
|
||||
T? get<T>(key) {
|
||||
var result = _preferences?.get(key);
|
||||
if (result != null) {
|
||||
return result as T;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
///获取JSON
|
||||
Map<String, dynamic>? getJson(key) {
|
||||
String? result = _preferences?.getString(key);
|
||||
if (result!=null && result!='') {
|
||||
return jsonDecode(result);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
///文中的StringUtil中的isNotEmpty的判断
|
||||
/// static isNotEmpty(String? str) {
|
||||
/// return str?.isNotEmpty ?? false;
|
||||
/// }
|
||||
///清除全部
|
||||
void clean() {
|
||||
_preferences?.clear();
|
||||
}
|
||||
///移除某一个
|
||||
void remove(key) {
|
||||
_preferences?.remove(key);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,826 @@
|
|||
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 不会被取消。
|
||||
|
||||
final Map<String, CancelToken> _pendingRequests = {}; // 正在请求列表
|
||||
|
||||
static Dio dio = Dio();
|
||||
// static Dio dio;
|
||||
|
||||
String _getBaseUrl() => 'http://api.test.jjsos.cn';
|
||||
/// 通用全局单例,第一次使用时初始化。
|
||||
XHttp._internal() {
|
||||
if ('' == dio.options.baseUrl) {
|
||||
dio = Dio(BaseOptions(
|
||||
baseUrl: _getBaseUrl(),
|
||||
// contentType: ,
|
||||
// responseType: ,
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
connectTimeout: const Duration(milliseconds: CONNECT_TIMEOUT),
|
||||
receiveTimeout: const Duration(milliseconds: RECEIVE_TIMEOUT),
|
||||
sendTimeout: const Duration(milliseconds: 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'].toString();
|
||||
String msg = (response?.data ?? {})['message'] ?? response.statusMessage;
|
||||
// // 静态数据 或者 根据后台实际返回结构解析,即 code == '200' 时,data 为有效数据。
|
||||
bool isSuccess = option.contentType != null && option.contentType!.contains("text") || code == '200';
|
||||
response.data = Result(response.data, isSuccess, response.statusCode!, msg, headers: response.headers);
|
||||
return handler.next(response);
|
||||
},
|
||||
onError: (DioException 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(DioException error) {
|
||||
// 也可以在此处根据状态码并处理错误信息,例如退出登录等等。
|
||||
String errorTypeInfo = '其他错误!';
|
||||
switch (error.type) {
|
||||
case DioExceptionType.connectionTimeout:
|
||||
errorTypeInfo = '连接超时!';
|
||||
break;
|
||||
case DioExceptionType.sendTimeout:
|
||||
errorTypeInfo = "请求超时!";
|
||||
break;
|
||||
case DioExceptionType.receiveTimeout:
|
||||
errorTypeInfo = "响应超时!";
|
||||
break;
|
||||
case DioExceptionType.badResponse:
|
||||
errorTypeInfo = "服务异常!";
|
||||
break;
|
||||
case DioExceptionType.cancel:
|
||||
errorTypeInfo = "请求取消!";
|
||||
break;
|
||||
case DioExceptionType.unknown:
|
||||
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['message']} \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 DioException) {
|
||||
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 = {} as Response;
|
||||
CancelToken requestToken = CancelToken();
|
||||
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;
|
||||
CancelToken requestToken = CancelToken();
|
||||
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(null, resultDialogConfig);
|
||||
}
|
||||
}
|
||||
|
||||
/// put 请求
|
||||
Future put(String url, [Map<String, dynamic>? data, resultDialogConfig, bool isCancelWhiteList = false]) async {
|
||||
// 可转为使用 request 代替,简化代码。
|
||||
Response response = {} as Response;
|
||||
CancelToken requestToken = {} as CancelToken ;
|
||||
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 = {} as Response;
|
||||
CancelToken requestToken = {} as CancelToken ;
|
||||
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 = {} as Response;
|
||||
CancelToken requestToken = {} as CancelToken ;
|
||||
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,
|
||||
required String baseUrl,
|
||||
}) async {
|
||||
XHttp.getInstance(baseUrl: baseUrl, msg: msg);
|
||||
Response response = {} as Response;
|
||||
CancelToken requestToken = {} as CancelToken ;
|
||||
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 = {} as Response;
|
||||
CancelToken requestToken = {} as CancelToken;
|
||||
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 = Duration(milliseconds: timeout);
|
||||
dio.options.receiveTimeout = Duration(milliseconds: timeout);
|
||||
dio.options.sendTimeout = Duration(milliseconds: timeout);
|
||||
return _instance;
|
||||
}
|
||||
|
||||
/// 设置当前的连接超时时间
|
||||
static XHttp setConnectTimeout(int timeout) {
|
||||
dio.options.connectTimeout = Duration(milliseconds: timeout);
|
||||
return _instance;
|
||||
}
|
||||
|
||||
/// 设置当前的接收超时时间
|
||||
static XHttp setReceiveTimeout(int timeout) {
|
||||
dio.options.receiveTimeout = Duration(milliseconds: timeout);
|
||||
return _instance;
|
||||
}
|
||||
|
||||
/// 设置当前的发送超时时间
|
||||
static XHttp setSendTimeout(int timeout) {
|
||||
dio.options.sendTimeout = Duration(milliseconds: 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
|
||||
// });
|
309
pubspec.lock
309
pubspec.lock
|
@ -6,7 +6,7 @@ packages:
|
|||
description:
|
||||
name: amap_flutter_base
|
||||
sha256: "9ef2439b8de7100cdd1b4357701b8ca8c059c0f2d9d0257b81750bbf0c6f53bb"
|
||||
url: "https://pub.dev"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "3.0.0"
|
||||
amap_flutter_location:
|
||||
|
@ -14,7 +14,7 @@ packages:
|
|||
description:
|
||||
name: amap_flutter_location
|
||||
sha256: f35ff00e196d579608e0bc28ccbc1f6f53787644702f947de941f775769cc701
|
||||
url: "https://pub.dev"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "3.0.0"
|
||||
amap_flutter_map:
|
||||
|
@ -22,7 +22,7 @@ packages:
|
|||
description:
|
||||
name: amap_flutter_map
|
||||
sha256: "9cebb0b2f5fc7d3ae0427e99c41edc883e8f5459f6a28bc850f0f9e16918cf2f"
|
||||
url: "https://pub.dev"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "3.0.0"
|
||||
async:
|
||||
|
@ -30,15 +30,23 @@ packages:
|
|||
description:
|
||||
name: async
|
||||
sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c"
|
||||
url: "https://pub.dev"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "2.11.0"
|
||||
basic_utils:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: basic_utils
|
||||
sha256: "2064b21d3c41ed7654bc82cc476fd65542e04d60059b74d5eed490a4da08fc6c"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "5.7.0"
|
||||
boolean_selector:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: boolean_selector
|
||||
sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66"
|
||||
url: "https://pub.dev"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "2.1.1"
|
||||
characters:
|
||||
|
@ -46,7 +54,7 @@ packages:
|
|||
description:
|
||||
name: characters
|
||||
sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605"
|
||||
url: "https://pub.dev"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.3.0"
|
||||
clock:
|
||||
|
@ -54,7 +62,7 @@ packages:
|
|||
description:
|
||||
name: clock
|
||||
sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf
|
||||
url: "https://pub.dev"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.1.1"
|
||||
collection:
|
||||
|
@ -62,36 +70,92 @@ packages:
|
|||
description:
|
||||
name: collection
|
||||
sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a
|
||||
url: "https://pub.dev"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.18.0"
|
||||
convert:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: convert
|
||||
sha256: "0f08b14755d163f6e2134cb58222dd25ea2a2ee8a195e53983d57c075324d592"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "3.1.1"
|
||||
crypto:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: crypto
|
||||
sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "3.0.3"
|
||||
cupertino_icons:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: cupertino_icons
|
||||
sha256: d57953e10f9f8327ce64a508a355f0b1ec902193f66288e8cb5070e7c47eeb2d
|
||||
url: "https://pub.dev"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.0.6"
|
||||
dio:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: dio
|
||||
sha256: "49af28382aefc53562459104f64d16b9dfd1e8ef68c862d5af436cc8356ce5a8"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "5.4.1"
|
||||
fake_async:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: fake_async
|
||||
sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78"
|
||||
url: "https://pub.dev"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.3.1"
|
||||
ffi:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: ffi
|
||||
sha256: "7bf0adc28a23d395f19f3f1eb21dd7cfd1dd9f8e1c50051c069122e6853bc878"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "2.1.0"
|
||||
file:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: file
|
||||
sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "7.0.0"
|
||||
fixnum:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: fixnum
|
||||
sha256: "25517a4deb0c03aa0f32fd12db525856438902d9c16536311e76cdc57b31d7d1"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.1.0"
|
||||
flutter:
|
||||
dependency: "direct main"
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
flutter_easyloading:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: flutter_easyloading
|
||||
sha256: ba21a3c883544e582f9cc455a4a0907556714e1e9cf0eababfcb600da191d17c
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "3.0.5"
|
||||
flutter_lints:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: flutter_lints
|
||||
sha256: a25a15ebbdfc33ab1cd26c63a6ee519df92338a9c10f122adda92938253bef04
|
||||
url: "https://pub.dev"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "2.0.3"
|
||||
flutter_plugin_android_lifecycle:
|
||||
|
@ -99,28 +163,81 @@ packages:
|
|||
description:
|
||||
name: flutter_plugin_android_lifecycle
|
||||
sha256: b068ffc46f82a55844acfa4fdbb61fad72fa2aef0905548419d97f0f95c456da
|
||||
url: "https://pub.dev"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "2.0.17"
|
||||
flutter_spinkit:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: flutter_spinkit
|
||||
sha256: b39c753e909d4796906c5696a14daf33639a76e017136c8d82bf3e620ce5bb8e
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "5.2.0"
|
||||
flutter_test:
|
||||
dependency: "direct dev"
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
flutter_web_plugins:
|
||||
dependency: transitive
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
http:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: http
|
||||
sha256: a2bbf9d017fcced29139daa8ed2bba4ece450ab222871df93ca9eec6f80c34ba
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.2.0"
|
||||
http_parser:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: http_parser
|
||||
sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "4.0.2"
|
||||
js:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: js
|
||||
sha256: c1b2e9b5ea78c45e1a0788d29606ba27dc5f71f019f32ca5140f61ef071838cf
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "0.7.1"
|
||||
json_annotation:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: json_annotation
|
||||
sha256: b10a7b2ff83d83c777edba3c6a0f97045ddadd56c944e1a23a3fdf43a1bf4467
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "4.8.1"
|
||||
lints:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: lints
|
||||
sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452"
|
||||
url: "https://pub.dev"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "2.1.1"
|
||||
logging:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: logging
|
||||
sha256: "623a88c9594aa774443aa3eb2d41807a48486b5613e67599fb4c41c0ad47c340"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.2.0"
|
||||
matcher:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: matcher
|
||||
sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e"
|
||||
url: "https://pub.dev"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "0.12.16"
|
||||
material_color_utilities:
|
||||
|
@ -128,7 +245,7 @@ packages:
|
|||
description:
|
||||
name: material_color_utilities
|
||||
sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41"
|
||||
url: "https://pub.dev"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "0.5.0"
|
||||
meta:
|
||||
|
@ -136,7 +253,7 @@ packages:
|
|||
description:
|
||||
name: meta
|
||||
sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e
|
||||
url: "https://pub.dev"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.10.0"
|
||||
path:
|
||||
|
@ -144,17 +261,113 @@ packages:
|
|||
description:
|
||||
name: path
|
||||
sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917"
|
||||
url: "https://pub.dev"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.8.3"
|
||||
path_provider_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_linux
|
||||
sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "2.2.1"
|
||||
path_provider_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_platform_interface
|
||||
sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "2.1.2"
|
||||
path_provider_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_windows
|
||||
sha256: "8bc9f22eee8690981c22aa7fc602f5c85b497a6fb2ceb35ee5a5e5ed85ad8170"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "2.2.1"
|
||||
platform:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: platform
|
||||
sha256: "12220bb4b65720483f8fa9450b4332347737cf8213dd2840d8b2c823e47243ec"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "3.1.4"
|
||||
plugin_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: plugin_platform_interface
|
||||
sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02"
|
||||
url: "https://pub.dev"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "2.1.8"
|
||||
pointycastle:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: pointycastle
|
||||
sha256: "43ac87de6e10afabc85c445745a7b799e04de84cebaa4fd7bf55a5e1e9604d29"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "3.7.4"
|
||||
shared_preferences:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: shared_preferences
|
||||
sha256: "81429e4481e1ccfb51ede496e916348668fd0921627779233bd24cc3ff6abd02"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "2.2.2"
|
||||
shared_preferences_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_android
|
||||
sha256: "8568a389334b6e83415b6aae55378e158fbc2314e074983362d20c562780fb06"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "2.2.1"
|
||||
shared_preferences_foundation:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_foundation
|
||||
sha256: "7708d83064f38060c7b39db12aefe449cb8cdc031d6062280087bc4cdb988f5c"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "2.3.5"
|
||||
shared_preferences_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_linux
|
||||
sha256: "9f2cbcf46d4270ea8be39fa156d86379077c8a5228d9dfdb1164ae0bb93f1faa"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "2.3.2"
|
||||
shared_preferences_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_platform_interface
|
||||
sha256: "22e2ecac9419b4246d7c22bfbbda589e3acf5c0351137d87dd2939d984d37c3b"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "2.3.2"
|
||||
shared_preferences_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_web
|
||||
sha256: "7b15ffb9387ea3e237bb7a66b8a23d2147663d391cafc5c8f37b2e7b4bde5d21"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "2.2.2"
|
||||
shared_preferences_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_windows
|
||||
sha256: "841ad54f3c8381c480d0c9b508b89a34036f512482c407e6df7a9c4aa2ef8f59"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "2.3.2"
|
||||
sky_engine:
|
||||
dependency: transitive
|
||||
description: flutter
|
||||
|
@ -165,15 +378,23 @@ packages:
|
|||
description:
|
||||
name: source_span
|
||||
sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c"
|
||||
url: "https://pub.dev"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.10.0"
|
||||
sprintf:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: sprintf
|
||||
sha256: "1fc9ffe69d4df602376b52949af107d8f5703b77cda567c4d7d86a0693120f23"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "7.0.0"
|
||||
stack_trace:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: stack_trace
|
||||
sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b"
|
||||
url: "https://pub.dev"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.11.1"
|
||||
stream_channel:
|
||||
|
@ -181,7 +402,7 @@ packages:
|
|||
description:
|
||||
name: stream_channel
|
||||
sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7
|
||||
url: "https://pub.dev"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "2.1.2"
|
||||
stream_transform:
|
||||
|
@ -189,7 +410,7 @@ packages:
|
|||
description:
|
||||
name: stream_transform
|
||||
sha256: "14a00e794c7c11aa145a170587321aedce29769c08d7f58b1d141da75e3b1c6f"
|
||||
url: "https://pub.dev"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "2.1.0"
|
||||
string_scanner:
|
||||
|
@ -197,7 +418,7 @@ packages:
|
|||
description:
|
||||
name: string_scanner
|
||||
sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde"
|
||||
url: "https://pub.dev"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.2.0"
|
||||
term_glyph:
|
||||
|
@ -205,7 +426,7 @@ packages:
|
|||
description:
|
||||
name: term_glyph
|
||||
sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84
|
||||
url: "https://pub.dev"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.2.1"
|
||||
test_api:
|
||||
|
@ -213,15 +434,31 @@ packages:
|
|||
description:
|
||||
name: test_api
|
||||
sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b"
|
||||
url: "https://pub.dev"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "0.6.1"
|
||||
typed_data:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: typed_data
|
||||
sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.3.2"
|
||||
uuid:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: uuid
|
||||
sha256: cd210a09f7c18cbe5a02511718e0334de6559871052c90a90c0cca46a4aa81c8
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "4.3.3"
|
||||
vector_math:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: vector_math
|
||||
sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803"
|
||||
url: "https://pub.dev"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "2.1.4"
|
||||
web:
|
||||
|
@ -229,9 +466,25 @@ packages:
|
|||
description:
|
||||
name: web
|
||||
sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152
|
||||
url: "https://pub.dev"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "0.3.0"
|
||||
win32:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: win32
|
||||
sha256: "464f5674532865248444b4c3daca12bd9bf2d7c47f759ce2617986e7229494a8"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "5.2.0"
|
||||
xdg_directories:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: xdg_directories
|
||||
sha256: faea9dee56b520b55a566385b84f2e8de55e7496104adada9962e0bd11bcff1d
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.0.4"
|
||||
sdks:
|
||||
dart: ">=3.2.6 <4.0.0"
|
||||
flutter: ">=3.7.0"
|
||||
flutter: ">=3.16.0"
|
||||
|
|
|
@ -33,6 +33,11 @@ dependencies:
|
|||
|
||||
amap_flutter_location: ^3.0.0
|
||||
amap_flutter_map: ^3.0.0
|
||||
dio: ^5.4.1
|
||||
flutter_easyloading: ^3.0.5
|
||||
shared_preferences: ^2.2.2
|
||||
basic_utils: ^5.7.0
|
||||
uuid: ^4.3.3
|
||||
|
||||
# The following adds the Cupertino Icons font to your application.
|
||||
# Use with the CupertinoIcons class for iOS style icons.
|
||||
|
|
Loading…
Reference in New Issue