当在Flutter中进行网络请求时,dio
是一个强大且常用的网络请求库。以下是使用dio
实现网络请求的基本配置,包括GET和POST请求,以及文件上传和下载的功能。
Dio 的基本配置
首先,确保在pubspec.yaml
文件中添加dio
库的依赖:
dependencies:
# https://github.com/flutterchina/dio
dio: ^5.3.3
然后运行flutter pub get
安装依赖。
接下来,需要在 Dart 文件中导入 dio 包:
import 'package:dio/dio.dart';
创建 Dio 实例
在使用 dio
之前,需要创建一个 Dio
实例。可以在整个应用程序中共享这个实例,也可以在需要的地方单独创建。
Dio dio = Dio();
发起 GET 请求
try {
Response response = await dio.get('https://api.example.com/data');
print(response.data);
} catch (e) {
print('Error: $e');
}
发起 POST 请求
try {
Response response = await dio.post(
'https://api.example.com/post',
data: {'key': 'value'},
);
print(response.data);
} catch (e) {
print('Error: $e');
}
文件上传
try {
FormData formData = FormData.fromMap({
'file': await MultipartFile.fromFile('/path/to/file.txt', filename: 'upload.txt'),
'other_field': 'other_value',
});
Response response = await dio.post(
'https://api.example.com/upload',
data: formData,
);
print(response.data);
} catch (e) {
print('Error: $e');
}
文件下载
try {
Response response = await dio.download(
'https://api.example.com/file',
'/path/to/save/file.txt',
onReceiveProgress: (received, total) {
if (total != -1) {
print((received / total * 100).toStringAsFixed(0) + "%");
}
},
);
print(response.data);
} catch (e) {
print('Error: $e');
}
配置 Dio
Dio dio = Dio(BaseOptions(
baseUrl: 'https://api.example.com',
connectTimeout: 5000, // 连接超时时间
receiveTimeout: 3000, // 接收超时时间
headers: {'Authorization': 'Bearer YourToken'}, // 全局请求头
contentType: Headers.formUrlEncodedContentType, // 设置请求格式
responseType: ResponseType.json, // 设置响应格式
));
下面是一个完整工具类代码,供大家参考使用:
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
}, onError: (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)? onError,
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) {
onError(_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)? onError,
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 DioException catch (e) {
_toggleLoading(showLoading, loadingMsg);
if (onError != null) {
onError(_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,
void Function(Response response)? onSuccess,
void Function(String error)? onError}) 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) {
onError(_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");
}
}