关于 flutter-dio
面向 Flutter 开发的 HTTP 网络技能,介绍使用 Dio 处理类型安全模型、拦截器、重试、缓存和令牌安全,也说明简单请求何时可用 http 包。
主要功能
- 01Dio 请求与响应模式
- 02类型安全的 JSON 模型
- 03身份验证与错误拦截器
- 04重试、超时和缓存指南
- 05安全存储令牌
- 06简单 API 的 http 包用法
适用场景
- 01构建生产环境的 Flutter API 客户端
- 02附加令牌并处理 401 刷新
- 03分类处理网络与服务器错误
- 04通过注入的 http.Client 测试请求
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ dhruvanbhalara/ skills --skill flutter-dio安装后在 Claude Code 中输入/flutter-dio确认已加载。
详细介绍
使用 Dio 进行网络通信
- 将 Dio 用作主要的 HTTP 客户端包。
- 所有请求和响应正文都使用类型安全的模型类,并提供
fromJson/toJson工厂。 - 使用带类型的异常妥善处理所有 HTTP 状态码(例如,
ServerException、NetworkException、UnauthorizedException)。 - 设置适当的请求超时时间(
connectTimeout、receiveTimeout、sendTimeout)。
Dio 拦截器
- 使用拦截器处理横切关注点:
- 身份验证拦截器:将访问令牌附加到请求头,并在收到 401 时处理令牌刷新。
- 日志记录拦截器:在调试模式下通过
AppLogger记录请求和响应。 - 错误拦截器:将
DioException转换为特定于领域的Failure类型。
- 通过
injectable集中注册拦截器,确保所有 API 调用的行为一致。
仓储模式
- DataSources 仅包含原始 Dio API 调用:不包含业务逻辑或映射。
- 对于网络数据,Repositories 负责协调远程 DataSources 与本地缓存。
重试与弹性
- 针对暂时性故障(例如,500 错误、超时)实现带指数退避的重试逻辑。
- 设置最大重试次数(默认:3 次)。
- 在适当情况下缓存响应,以减少网络调用并改善离线体验。
性能
- 对大型响应(> 1 MB),使用
compute()在后台 isolate 中解析 JSON。 - 不要使用同步网络操作阻塞 UI 线程。
安全
- 使用
flutter_secure_storage存储令牌:切勿将其存放在源代码或SharedPreferences中。 - 所有 API 通信都必须使用 HTTPS。
替代方案:http 包
对于不需要拦截器、缓存或重试逻辑的简单 REST 调用,请使用 http 而非 Dio。
Dio 与 http 对比
| 标准 | http | Dio |
|---|---|---|
| 拦截器 | 不支持 | 支持完整拦截器链 |
| 重试逻辑 | 手动处理 | 内置并支持退避 |
| 响应缓存 | 手动处理 | 提供插件 |
| FormData / Multipart | 手动处理 | 内置 |
| 取消请求 | 不支持 | 支持 CancelToken |
| 依赖项 | 最少(1 个包) | 较重 |
| 使用场景 | 简单的 CRUD API | 生产环境 API 客户端 |
原型和简单的获取并显示场景使用 http。对于需要身份验证、重试和缓存的生产环境 API 客户端,使用 Dio。
http 基本模式
import 'dart:convert';
import 'package:http/http.dart' as http;
// GET request
Future<Map<String, dynamic>> fetchData(http.Client client) async {
final response = await client.get(
Uri.parse('https://api.example.com/data'),
headers: {'Accept': 'application/json'},
);
if (response.statusCode == 200) {
return jsonDecode(response.body) as Map<String, dynamic>;
} else {
throw Exception('Failed to fetch: ${response.statusCode}');
}
}
// POST request
Future<void> createItem(http.Client client, Map<String, dynamic> body) async {
final response = await client.post(
Uri.parse('https://api.example.com/items'),
headers: {'Content-Type': 'application/json'},
body: jsonEncode(body),
);
if (response.statusCode != 201) {
throw Exception('Failed to create: ${response.statusCode}');
}
}
测试:始终将 http.Client 作为参数传入(而不是直接调用 http.get()),以便在测试中注入模拟对象。
评论
支持 Markdown