Dio HTTP 请求完整教程
目录
- [什么是 Dio](#什么是 Dio)
- 环境安装
- [创建 Dio 实例](#创建 Dio 实例)
- 基础请求方法
- 请求配置
- 响应处理
- 拦截器
- 错误处理
- 取消请求
- 文件上传与下载
- [与 Riverpod 集成](#与 Riverpod 集成)
- 最佳实践
什么是 Dio
Dio 是一个强大的 Dart HTTP 客户端,专为 Flutter 设计。它提供了比 Dart 官方 http 包更丰富的功能:
- 支持全局配置和拦截器
- 支持请求/响应拦截、错误统一处理
- 支持文件上传、下载进度监听
- 支持请求取消
- 支持自定义适配器、转换器
环境安装
在 pubspec.yaml 中添加依赖:
yaml
dependencies:
dio: ^5.5.0
然后执行:
bash
flutter pub get
创建 Dio 实例
推荐将 Dio 实例封装为单例,便于全局统一配置。
dart
import 'package:dio/dio.dart';
class DioClient {
static final DioClient _instance = DioClient._internal();
late final Dio dio;
factory DioClient() => _instance;
DioClient._internal() {
dio = Dio(
BaseOptions(
baseUrl: 'https://api.example.com/v1',
connectTimeout: const Duration(seconds: 10),
receiveTimeout: const Duration(seconds: 10),
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
),
);
}
}
使用:
dart
final response = await DioClient().dio.get('/users');
基础请求方法
GET 请求
dart
Future<List<User>> fetchUsers() async {
final response = await DioClient().dio.get('/users');
final List<dynamic> data = response.data;
return data.map((json) => User.fromJson(json)).toList();
}
带查询参数
dart
Future<List<User>> searchUsers(String keyword) async {
final response = await DioClient().dio.get(
'/users',
queryParameters: {'q': keyword, 'page': 1, 'limit': 20},
);
return (response.data as List).map((e) => User.fromJson(e)).toList();
}
POST 请求
dart
Future<User> createUser(String name, String email) async {
final response = await DioClient().dio.post(
'/users',
data: {'name': name, 'email': email},
);
return User.fromJson(response.data);
}
PUT / PATCH / DELETE
dart
// 更新全部字段
await DioClient().dio.put('/users/1', data: {'name': 'Tom'});
// 更新部分字段
await DioClient().dio.patch('/users/1', data: {'name': 'Tom'});
// 删除
await DioClient().dio.delete('/users/1');
请求配置
单请求覆盖配置
dart
final response = await DioClient().dio.get(
'/slow-endpoint',
options: Options(
receiveTimeout: const Duration(seconds: 30),
headers: {'X-Custom-Header': 'value'},
),
);
常用配置项
dart
BaseOptions(
baseUrl: 'https://api.example.com',
connectTimeout: const Duration(seconds: 5),
receiveTimeout: const Duration(seconds: 5),
sendTimeout: const Duration(seconds: 5),
headers: {'Authorization': 'Bearer $token'},
responseType: ResponseType.json,
validateStatus: (status) => status != null && status < 500,
)
响应处理
Dio 的 Response 对象包含:
data:响应体statusCode:HTTP 状态码statusMessage:状态信息headers:响应头requestOptions:请求配置
dart
final response = await DioClient().dio.get('/users/1');
if (response.statusCode == 200) {
final user = User.fromJson(response.data);
}
拦截器
拦截器常用于:统一添加 token、打印日志、处理 401 重试等。
dart
DioClient._internal() {
dio = Dio(BaseOptions(baseUrl: 'https://api.example.com'));
dio.interceptors.add(
InterceptorsWrapper(
onRequest: (options, handler) async {
// 添加 Token
final token = await _getToken();
options.headers['Authorization'] = 'Bearer $token';
return handler.next(options);
},
onResponse: (response, handler) {
// 统一处理响应
debugPrint('Response: ${response.data}');
return handler.next(response);
},
onError: (DioException e, handler) {
// 统一处理错误
if (e.response?.statusCode == 401) {
// 跳转登录或刷新 token
}
return handler.next(e);
},
),
);
}
日志拦截器
dart
import 'package:dio/io.dart';
dio.interceptors.add(LogInterceptor(
requestBody: true,
responseBody: true,
));
错误处理
Dio 请求失败时会抛出 DioException。
dart
Future<User?> fetchUser(String id) async {
try {
final response = await DioClient().dio.get('/users/$id');
return User.fromJson(response.data);
} on DioException catch (e) {
switch (e.type) {
case DioExceptionType.connectionTimeout:
debugPrint('连接超时');
break;
case DioExceptionType.badResponse:
debugPrint('服务器错误: ${e.response?.statusCode}');
break;
case DioExceptionType.cancel:
debugPrint('请求被取消');
break;
default:
debugPrint('请求失败: ${e.message}');
}
return null;
} catch (e) {
debugPrint('未知错误: $e');
return null;
}
}
封装 Result 类型
dart
sealed class Result<T> {}
class Success<T> extends Result<T> {
Success(this.data);
final T data;
}
class Failure<T> extends Result<T> {
Failure(this.message);
final String message;
}
Future<Result<User>> fetchUserSafe(String id) async {
try {
final response = await DioClient().dio.get('/users/$id');
return Success(User.fromJson(response.data));
} on DioException catch (e) {
return Failure(e.message ?? '请求失败');
}
}
取消请求
使用 CancelToken 取消未完成的请求。
dart
class UserRepository {
CancelToken? _cancelToken;
Future<void> fetchUsers() async {
_cancelToken = CancelToken();
try {
final response = await DioClient().dio.get(
'/users',
cancelToken: _cancelToken,
);
// 处理数据
} on DioException catch (e) {
if (CancelToken.isCancel(e)) {
debugPrint('请求已取消');
}
}
}
void cancel() {
_cancelToken?.cancel();
_cancelToken = null;
}
}
文件上传与下载
文件上传
dart
Future<void> uploadAvatar(String filePath) async {
final formData = FormData.fromMap({
'file': await MultipartFile.fromFile(filePath, filename: 'avatar.jpg'),
});
final response = await DioClient().dio.post(
'/upload',
data: formData,
onSendProgress: (sent, total) {
debugPrint('上传进度: ${(sent / total * 100).toStringAsFixed(0)}%');
},
);
}
文件下载
dart
Future<void> downloadFile(String url, String savePath) async {
await DioClient().dio.download(
url,
savePath,
onReceiveProgress: (received, total) {
if (total != -1) {
debugPrint('下载进度: ${(received / total * 100).toStringAsFixed(0)}%');
}
},
);
}
与 Riverpod 集成
将 Dio 封装为 Provider,可在 Riverpod 中统一管理。
dart
import 'package:dio/dio.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'dio_provider.g.dart';
@riverpod
Dio dio(DioRef ref) {
final dio = Dio(
BaseOptions(
baseUrl: 'https://api.example.com/v1',
connectTimeout: const Duration(seconds: 10),
),
);
dio.interceptors.add(LogInterceptor(requestBody: true, responseBody: true));
return dio;
}
在 Repository 中注入:
dart
@riverpod
Future<User> user(UserRef ref, String id) async {
final dio = ref.watch(dioProvider);
final response = await dio.get('/users/$id');
return User.fromJson(response.data);
}
在 Widget 中使用:
dart
final asyncUser = ref.watch(userProvider('123'));
return asyncUser.when(
data: (user) => Text(user.name),
loading: () => const CircularProgressIndicator(),
error: (err, _) => Text('错误: $err'),
);
最佳实践
- 单例封装:全局使用同一个 Dio 实例,统一 baseUrl、超时、拦截器。
- 统一错误处理:在拦截器或 Repository 层处理常见错误,Widget 只负责展示。
- 模型化数据 :将
response.data转换为 Dart 模型类,避免直接使用Map。 - 使用 CancelToken:页面销毁或用户刷新时取消旧请求,防止内存泄漏和状态异常。
- 环境隔离:通过环境变量切换 baseUrl(开发/测试/生产)。
- 与 Riverpod 结合:将 Dio 和 API 方法声明为 Provider,便于测试和依赖替换。
- 避免在 UI 层直接调用 Dio:由 Repository/Service 层封装网络请求,UI 只调用 Riverpod 状态。
结语
Dio 是 Flutter 网络请求的事实标准。掌握 Dio 的基础请求、拦截器、错误处理、取消请求、文件操作以及与 Riverpod 的结合,可以覆盖绝大多数移动应用的网络层需求。建议将 Dio 实例、API 方法、数据模型分层管理,保持代码清晰可维护。