一、为什么选择 Dio?
Flutter 内置的 http 包可以满足简单请求,但在实际开发中,我们往往需要:
- 统一的 BaseURL 和超时配置
- 拦截器(请求头注入、Token 刷新、日志打印)
- 更清晰的 错误处理
- 支持 GET / POST / 文件上传 等多种请求方式
Dio 是 Flutter/Dart 生态中最流行的 HTTP 客户端库,功能强大、API 设计友好。本项目使用的是 Dio 5.x (^5.9.0)。
二、项目依赖配置
在 pubspec.yaml 中添加依赖:
yaml
dependencies:
dio: ^5.9.0
安装依赖:
bash
flutter pub get
三、第一步:最简单的 GET 请求
项目中的 lib/Dio的简单使用.dart 展示了最基础的用法------创建一个 Dio 实例,直接发起 GET 请求:
dart
import 'package:dio/dio.dart';
void main(List<String> args) {
Dio().get("https://geek.itheima.net/v1_0/channels").then((res){
print(res);
});
}
要点:
| 概念 | 说明 |
|---|---|
Dio() |
创建一个 Dio 实例 |
.get(url) |
发起 GET 请求,返回 Future<Response> |
.then() |
用回调处理响应(也可用 async/await) |
这种方式适合快速验证接口,但每次新建 Dio() 实例、硬编码完整 URL,不利于维护。接下来我们把它封装成工具类。
四、封装 DioUtils 工具类
lib/Dio工具封装案例.dart 和 lib/main.dart 中都实现了 DioUtils,核心思路一致:单例 Dio 实例 + 统一配置 + 拦截器 + 对外暴露简洁 API。
4.1 基础配置:BaseURL 与超时
dart
final Dio _dio = Dio();
_dio.options
..baseUrl = "https://geek.itheima.net/v1_0/"
..connectTimeout = Duration(seconds: 10) // 连接超时
..sendTimeout = Duration(seconds: 10) // 发送超时
..receiveTimeout = Duration(seconds: 10); // 接收超时
这里使用了 Dart 的 级联操作符 ..,可以在同一对象上连续赋值,写法更简洁。
配置 baseUrl 后,请求时只需传入相对路径 "channels",Dio 会自动拼接为完整 URL。
4.2 拦截器:请求 / 响应 / 错误三阶段
dart
_dio.interceptors.add(InterceptorsWrapper(
onRequest: (options, handler) {
// 请求发出前:可加 Token、公共 Header、日志等
handler.next(options);
},
onResponse: (response, handler) {
if (response.statusCode! >= 200 && response.statusCode! <= 300) {
handler.next(response); // 状态码正常,继续传递
return;
}
// 非 2xx 状态码,转为异常
handler.reject(DioException(
requestOptions: response.requestOptions,
response: response,
message: "状态码${response.statusCode}",
));
},
onError: (DioException e, handler) {
handler.reject(e); // 网络错误、超时等
},
));
拦截器的典型用途:
请求流程:
onRequest → 服务器 → onResponse / onError
↑ ↓
注入 Token 统一解析业务码
打印日志 错误提示 / 重试
4.3 对外暴露 GET 方法
Dio工具封装案例.dart 中的版本会直接返回 response.data,并在内部捕获异常:
dart
Future<dynamic> get(String url, {Map<String, dynamic>? queryParameters}) async {
try {
Response response = await _dio.get(url, queryParameters: queryParameters);
return response.data;
} on DioException catch (e) {
return e.message;
}
}
main.dart 中的版本则返回完整的 Response,由调用方自行解析:
dart
Future<Response<dynamic>> get(String url, {Map<String, dynamic>? queryParameters}) async {
return await _dio.get(url, queryParameters: queryParameters);
}
两种写法各有取舍:前者调用更简单,后者保留更多响应信息(状态码、Header 等)。
五、在 Flutter UI 中展示数据
main.dart 将 DioUtils 与 Flutter 界面结合,请求频道列表并用 GridView 展示:
dart
class _MainPageState extends State<MainPage> {
List<Map<String, dynamic>> _list = [];
@override
void initState() {
super.initState();
_getChannels();
}
void _getChannels() async {
Response<dynamic> res = await DioUtils().get("channels");
Map<String, dynamic> result = res.data as Map<String, dynamic>;
List data = result["data"]["channels"] as List;
_list = data.cast<Map<String, dynamic>>();
setState(() {}); // 触发 UI 刷新
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text("Dio工具类显示数据")),
body: GridView.extent(
maxCrossAxisExtent: 140,
children: List.generate(_list.length, (index) {
return ChannelItem(item: _list[index]);
}),
),
),
);
}
}
数据流:
initState()
↓
DioUtils().get("channels")
↓
解析 JSON → result["data"]["channels"]
↓
setState() → GridView 渲染 ChannelItem
接口返回的数据结构大致为:
json
{
"data": {
"channels": [
{ "id": 1, "name": "推荐" },
{ "id": 2, "name": "HTML" }
]
}
}
六、项目文件结构一览
lib/
├── Dio的简单使用.dart # 入门:一行 GET 请求
├── Dio工具封装案例.dart # 进阶:DioUtils 封装 + 命令行测试
└── main.dart # 实战:DioUtils + Flutter UI 展示
学习路径建议:简单使用 → 工具封装 → UI 集成,由浅入深。
七、实现效果


八、总结
| 阶段 | 文件 | 核心内容 |
|---|---|---|
| 入门 | Dio的简单使用.dart |
Dio().get(url) 一行发请求 |
| 封装 | Dio工具封装案例.dart |
BaseURL、超时、拦截器、get() 方法 |
| 实战 | main.dart |
异步请求 + JSON 解析 + GridView 展示 |
Dio 的核心价值在于 可配置、可拦截、可扩展 。本项目从 7 行的简单示例,到带拦截器的工具类,再到完整的 UI 数据流,构成了一条清晰的学习曲线。掌握这套模式后,POST 请求、文件上传、下载进度等能力都可以在同一套 DioUtils 上扩展。