当在Flutter中进行网络请求时,dio
是一个强大且常用的网络请求库。以下是使用dio
实现网络请求的基本配置,包括GET和POST请求,以及文件上传和下载的功能。
首先,确保在pubspec.yaml
文件中添加dio
库的依赖:
dependencies:
# https://github.com/flutterchina/dio
dio: ^5.3.3
然后运行flutter pub get
安装依赖。
下面是完整工具类代码:
import 'package:cookie_jar/cookie_jar.dart';
import 'package:dio/dio.dart';
import 'package:dio_cookie_manager/dio_cookie_manager.dart';
import 'package:get/get.dart' as getx;
import '../../weight/loading.dart';
import 'api.dart';
class HttpUtil {
static HttpUtil? instance;
late Dio dio;
late BaseOptions options;
CancelToken cancelToken = CancelToken();
static HttpUtil getInstance() {
instance ??= HttpUtil();
return instance!;
}
/*
* config it and create
*/
HttpUtil() {
//BaseOptions、Options、RequestOptions 都可以配置参数,优先级别依次递增,且可以根据优先级别覆盖参数
options = BaseOptions(
//请求基地址,可以包含子路径
baseUrl: Api.BASE_URL,
//连接服务器超时时间,单位是秒.
connectTimeout: const Duration(seconds: 10),
//响应流上前后两次接受到数据的间隔,单位为秒。
receiveTimeout: const Duration(seconds: 5),
//Http请求头.
headers: {
"version": "1.0.0"
},
//请求的Content-Type,默认值是"application/json; charset=utf-8",Headers.formUrlEncodedContentType会自动编码请求体.
contentType: Headers.formUrlEncodedContentType,
//表示期望以那种格式(方式)接受响应数据。接受四种类型 `json`, `stream`, `plain`, `bytes`. 默认值是 `json`,
responseType: ResponseType.plain,
);
dio = Dio(options);
//Cookie管理
final cookieJar = CookieJar();
dio.interceptors.add(CookieManager(cookieJar));
//添加拦截器
dio.interceptors.add(InterceptorsWrapper(
onRequest: (RequestOptions options, RequestInterceptorHandler handler) {
print("请求之前 header = ${options.headers.toString()}");
// 打印查询参数
if (options.queryParameters != null) {
print("Query Parameters: ${options.queryParameters}");
}
// 如果你想完成请求并返回一些自定义数据,你可以使用 `handler.resolve(response)`。
// 如果你想终止请求并触发一个错误,你可以使用 `handler.reject(error)`。
return handler.next(options); //continue
}, onResponse: (Response response, ResponseInterceptorHandler handler) {
print("响应之前");
// 如果你想终止请求并触发一个错误,你可以使用 `handler.reject(error)`。
return handler.next(response); // continue
}, one rror: (DioException e, ErrorInterceptorHandler handler) {
print("错误之前");
// 如果你想完成请求并返回一些自定义数据,你可以使用 `handler.resolve(response)`。
return handler.next(e);
}));
}
/*
* GET请求
*/
Future<Response?> getRequest(
String url, {
Map<String, dynamic>? parameters,
Options? options,
CancelToken? cancelToken,
void Function(Response response)? onSuccess,
void Function(String error)? one rror,
bool showLoading = false,
String loadingMsg = '请稍后...',
}) async {
try {
_toggleLoading(showLoading, loadingMsg);
final response = await dio.get(
url,
queryParameters: parameters,
options: options,
cancelToken: cancelToken,
);
_toggleLoading(showLoading, loadingMsg);
if (onSuccess != null) {
if (response.statusCode == 200) {
onSuccess(response);
} else {
// 处理其他状态码的逻辑
// ...
}
}
return response;
} on DioException catch (e) {
_toggleLoading(showLoading, loadingMsg);
if (onError != null) {
one rror(_formatError(e));
}
return null;
}
}
void _toggleLoading(bool showLoading, String loadingMsg) {
if (showLoading) {
LoadingIndicator.show(getx.Get.context!, title: loadingMsg);
} else {
LoadingIndicator.hide();
}
}
/*
* POST请求
*/
Future<Response?> postRequest(
String url, {
Map<String, dynamic>? parameters,
dynamic data,
Options? options,
CancelToken? cancelToken,
void Function(Response response)? onSuccess,
void Function(String error)? one rror,
bool showLoading = false,
String loadingMsg = '请稍后...',
}) async {
try {
_toggleLoading(showLoading, loadingMsg);
final response = await dio.post(
url,
data: data,
queryParameters: parameters,
options: options,
cancelToken: cancelToken,
);
_toggleLoading(showLoading, loadingMsg);
if (onSuccess != null) {
if (response.statusCode == 200) {
onSuccess(response);
} else {
// 处理其他状态码的逻辑
// ...
}
}
return response;
} on DioError catch (e) {
_toggleLoading(showLoading, loadingMsg);
if (onError != null) {
one rror(_formatError(e));
}
return null;
}
}
/*
* 下载文件
*/
Future<dynamic> downloadFile(String urlPath, String savePath) async {
Response? response;
try {
response = await dio.download(
urlPath,
savePath,
onReceiveProgress: _onDownloadProgress,
);
} on DioError catch (e) {
_formatError(e);
}
return response?.data;
}
// 提取进度回调
void _onDownloadProgress(int count, int total) {
// 进度
print("$count $total");
}
/*
* 上传文件
*/
Future<void> uploadFile(url, FormData formData,
{String? accessToken,
Function(Response response)? onSuccess,
Function(String error)? one rror}) async {
try {
late Response response;
response = await dio.post(
url,
data: formData,
options: Options(
headers: {'Authorization': 'Bearer $accessToken'},
),
);
if (onSuccess != null) {
if (response.statusCode == 200) {
onSuccess(response);
} else {
// 处理其他状态码的逻辑
// ...
}
}
print('上传成功: ${response.data}');
} on DioException catch (e) {
if (onError != null) {
one rror(_formatError(e));
}
print('上传失败: $e');
}
}
/*
* error统一处理
*/
String _formatError(DioException e) {
String errorMsg = '';
if (e.type == DioExceptionType.connectionTimeout) {
errorMsg = '连接超时';
} else if (e.type == DioExceptionType.sendTimeout) {
errorMsg = '请求超时';
} else if (e.type == DioExceptionType.receiveTimeout) {
errorMsg = '响应超时';
} else if (e.type == DioExceptionType.badResponse) {
errorMsg = '错误响应';
} else if (e.type == DioExceptionType.cancel) {
errorMsg = '请求取消';
} else if (e.type == DioExceptionType.connectionError) {
errorMsg = '无法连接服务器';
} else {
errorMsg = '未知错误';
}
print(errorMsg);
return errorMsg;
}
/*
* 取消请求
*
* 同一个cancel token 可以用于多个请求,当一个cancel token取消时,所有使用该cancel token的请求都会被取消。
* 所以参数可选
*/
void cancelRequests(CancelToken token) {
token.cancel("cancelled");
}
}