Flutter网络请求封装:高效、灵活、易用的Dio工具类
在Flutter开发中,网络请求是必不可少的功能。为了简化代码、提高开发效率,我们通常会封装一个网络请求工具类。本文基于Dio
库,详细介绍如何封装一个高效、灵活、易用的网络请求工具类,支持以下功能:
- 单例模式:确保全局只有一个
Dio
实例,避免资源浪费。 - 动态配置:支持运行时动态修改
baseUrl
、headers
等配置。 - 拦截器:内置日志拦截器,并支持添加自定义拦截器。
- 错误处理:提供详细的错误信息,支持自定义错误处理逻辑。
- 文件上传和下载:封装了文件上传和下载功能。
- 取消请求:支持取消正在进行的请求。
- 模块化设计:代码结构清晰,便于维护和扩展。
通过本文,你将学会如何封装一个功能强大的网络请求工具类,并直接应用到你的Flutter项目中。
代码实现
import 'package:dio/dio.dart';
import 'package:flutter/foundation.dart';
class HttpUtil {
// 单例模式
static final HttpUtil _instance = HttpUtil._internal();
factory HttpUtil() => _instance;
HttpUtil._internal() {
_init();
}
late Dio _dio;
final List<Interceptor> _interceptors = []; // 自定义拦截器列表
CancelToken _cancelToken = CancelToken(); // 用于取消请求
// 初始化
void _init() {
_dio = Dio(BaseOptions(
baseUrl: 'https://your-api-url.com', // 默认基础地址
connectTimeout: const Duration(seconds: 5), // 连接超时时间
receiveTimeout: const Duration(seconds: 5), // 接收数据超时时间
headers: {
'Content-Type': 'application/json; charset=UTF-8',
},
));
// 添加默认拦截器
_dio.interceptors.add(InterceptorsWrapper(
onRequest: (options, handler) {
if (kDebugMode) {
print('Request: ${options.method} ${options.path}');
}
return handler.next(options);
},
onResponse: (response, handler) {
if (kDebugMode) {
print('Response: ${response.statusCode} ${response.data}');
}
return handler.next(response);
},
onError: (DioException e, handler) {
if (kDebugMode) {
print('Error: ${e.message}');
}
return handler.next(e);
},
));
// 添加自定义拦截器
for (var interceptor in _interceptors) {
_dio.interceptors.add(interceptor);
}
}
// 添加自定义拦截器
void addInterceptor(Interceptor interceptor) {
_interceptors.add(interceptor);
_dio.interceptors.add(interceptor);
}
// 动态更新基础配置
void updateBaseConfig({
String? baseUrl,
Duration? connectTimeout,
Duration? receiveTimeout,
Map<String, dynamic>? headers,
}) {
_dio.options.baseUrl = baseUrl ?? _dio.options.baseUrl;
_dio.options.connectTimeout = connectTimeout ?? _dio.options.connectTimeout;
_dio.options.receiveTimeout = receiveTimeout ?? _dio.options.receiveTimeout;
_dio.options.headers = headers ?? _dio.options.headers;
}
// GET请求
Future<Response> get(
String path, {
Map<String, dynamic>? queryParameters,
Options? options,
CancelToken? cancelToken,
}) async {
return _request(
path,
method: 'GET',
queryParameters: queryParameters,
options: options,
cancelToken: cancelToken,
);
}
// POST请求
Future<Response> post(
String path, {
dynamic data,
Map<String, dynamic>? queryParameters,
Options? options,
CancelToken? cancelToken,
}) async {
return _request(
path,
method: 'POST',
data: data,
queryParameters: queryParameters,
options: options,
cancelToken: cancelToken,
);
}
// PUT请求
Future<Response> put(
String path, {
dynamic data,
Map<String, dynamic>? queryParameters,
Options? options,
CancelToken? cancelToken,
}) async {
return _request(
path,
method: 'PUT',
data: data,
queryParameters: queryParameters,
options: options,
cancelToken: cancelToken,
);
}
// DELETE请求
Future<Response> delete(
String path, {
dynamic data,
Map<String, dynamic>? queryParameters,
Options? options,
CancelToken? cancelToken,
}) async {
return _request(
path,
method: 'DELETE',
data: data,
queryParameters: queryParameters,
options: options,
cancelToken: cancelToken,
);
}
// 文件上传
Future<Response> upload(
String path, {
required String filePath,
Map<String, dynamic>? data,
Map<String, dynamic>? queryParameters,
Options? options,
CancelToken? cancelToken,
}) async {
final formData = FormData.fromMap({
...data ?? {},
'file': await MultipartFile.fromFile(filePath),
});
return _request(
path,
method: 'POST',
data: formData,
queryParameters: queryParameters,
options: options,
cancelToken: cancelToken,
);
}
// 文件下载
Future<Response> download(
String urlPath,
String savePath, {
Map<String, dynamic>? queryParameters,
Options? options,
CancelToken? cancelToken,
}) async {
try {
final response = await _dio.download(
urlPath,
savePath,
queryParameters: queryParameters,
options: options,
cancelToken: cancelToken ?? _cancelToken,
);
return response;
} on DioException catch (e) {
throw _handleError(e);
}
}
// 通用请求方法
Future<Response> _request(
String path, {
required String method,
dynamic data,
Map<String, dynamic>? queryParameters,
Options? options,
CancelToken? cancelToken,
}) async {
try {
final response = await _dio.request(
path,
data: data,
queryParameters: queryParameters,
options: Options(method: method),
cancelToken: cancelToken ?? _cancelToken,
);
return response;
} on DioException catch (e) {
throw _handleError(e);
}
}
// 取消请求
void cancelRequests({CancelToken? cancelToken}) {
if (cancelToken == null) {
_cancelToken.cancel('Request cancelled');
_cancelToken = CancelToken(); // 重置CancelToken
} else {
cancelToken.cancel('Request cancelled');
}
}
// 错误处理
String _handleError(DioException e) {
switch (e.type) {
case DioExceptionType.connectionTimeout:
return '连接超时';
case DioExceptionType.sendTimeout:
return '发送请求超时';
case DioExceptionType.receiveTimeout:
return '接收数据超时';
case DioExceptionType.badResponse:
return '服务器返回错误: ${e.response?.statusCode}';
case DioExceptionType.cancel:
return '请求已取消';
case DioExceptionType.unknown:
return '未知错误: ${e.message}';
default:
return '网络错误: ${e.message}';
}
}
}
使用示例
void fetchData() async {
try {
final response = await HttpUtil().get('/api/data');
print('Data: ${response.data}');
} catch (e) {
print('Error: $e');
}
}
void uploadFile() async {
try {
final response = await HttpUtil().upload(
'/api/upload',
filePath: '/path/to/file',
);
print('Upload Response: ${response.data}');
} catch (e) {
print('Error: $e');
}
}
void cancelRequest() {
HttpUtil().cancelRequests();
}
总结
通过封装这样一个网络请求工具类,我们可以显著提高Flutter项目的开发效率,减少重复代码,同时增强代码的可维护性和扩展性。希望本文对你有所帮助