任何开发语言都离不开网络请求,Flutter亦是如此。http库是Flutter官方的一个网络请求库,功能比较基础,在日常开发中直接使用的也相对比较少,毕竟有一个功能强大的Dio库。但作为一个初学者,其实有必要了解一下http库大概是一个什么样子的。
一、安装 http 包
在 pubspec.yaml 中添加依赖:
dependencies:
http: ^1.1.0 # 检查最新版本
运行 flutter pub get。
二、基本用法
了解网络请求的同学可能知道,基本的网络请求无非就是了解一下这几种基本的用法:
- get请求
- post请求
- delete请求
- put请求
- 文件上传
1. 发送 GET 请求
Future<void> fetchData() async {
final url = Uri.parse('https://jsonplaceholder.typicode.com/posts/1');
try {
final response = await http.get(url);
if (response.statusCode == 200) {
print('响应数据: ${response.body}');
} else {
print('请求失败: ${response.statusCode}');
}
} catch (e) {
print('网络错误: $e');
}
}
2. 发送 POST 请求
Future<void> postData() async {
final url = Uri.parse('https://jsonplaceholder.typicode.com/posts');
final headers = {'Content-Type': 'application/json'};
final body = jsonEncode({'title': 'foo', 'body': 'bar', 'userId': 1});
try {
final response = await http.post(url, headers: headers, body: body);
if (response.statusCode == 201) {
print('创建成功: ${response.body}');
}
} catch (e) {
print('请求失败: $e');
}
}
3. 发送 put 请求
Future<void> updateData() async {
final url = Uri.parse('https://jsonplaceholder.typicode.com/posts/1');
final headers = {'Content-Type': 'application/json'};
final body = jsonEncode({'title': 'updated title', 'body': 'updated body'});
try {
final response = await http.put(url, headers: headers, body: body);
if (response.statusCode == 200) {
print('PUT 响应数据: ${response.body}');
}
} catch (e) {
print('PUT 请求失败: $e');
}
}
4. 发送delete请求
Future<void> deleteData() async {
final url = Uri.parse('https://jsonplaceholder.typicode.com/posts/1');
try {
final response = await http.delete(url);
if (response.statusCode == 200) {
print('DELETE 成功');
}
} catch (e) {
print('DELETE 请求失败: $e');
}
}
5. 上传文件
上传文件也是一个Post请求,只是传输的数据类型和普通的post不太一样, 需手动处理 multipart/form-data:
Future<void> uploadFile() async {
final url = Uri.parse('https://api.example.com/upload');
//创建一个request
final request = http.MultipartRequest('POST', url);
// 添加文件
request.files.add(
await http.MultipartFile.fromPath('file', '/path/to/file.jpg'),
);
// 添加字段
request.fields['key'] = 'value';
try {
// 发送一个封装好的request
final response = await request.send();
if (response.statusCode == 200) {
print('上传成功');
}
} catch (e) {
print('上传失败: $e');
}
}
6、文件下载
其实就是一个get的网络请求,加上文件保存的过程。 通过流式处理保存文件:
Future<void> downloadFile() async {
final url = Uri.parse('https://example.com/file.zip');
final response = await http.get(url);
if (response.statusCode == 200) {
final file = File('/local/path/file.zip');
await file.writeAsBytes(response.bodyBytes);
print('文件已保存');
}
}
三、处理 JSON 数据
使用 dart:convert 库解析和编码 JSON:
1. 解析响应数据
import 'dart:convert';
//解析数据,需要先转化为json类型
void parseJson(String responseBody) {
final jsonData = jsonDecode(responseBody);
print('标题: ${jsonData['title']}');
}
2. 自定义模型类
class Post {
final int userId;
final int id;
final String title;
final String body;
Post({required this.userId, required this.id, required this.title, required this.body});
factory Post.fromJson(Map<String, dynamic> json) {
return Post(
userId: json['userId'],
id: json['id'],
title: json['title'],
body: json['body'],
);
}
}
// 使用示例
final post = Post.fromJson(jsonDecode(response.body));
四、高级用法
1. 设置请求头
final headers = {
'Authorization': 'Bearer your_token',
'Content-Type': 'application/json',
};
final response = await http.get(url, headers: headers);
2. 处理查询参数
使用 Uri 构建带查询参数的 URL:
final url = Uri.parse('https://api.example.com/data').replace(
queryParameters: {'page': '1', 'limit': '10'},
);
3. 超时设置
通过 Future.timeout 实现:
try {
final response = await http.get(url).timeout(Duration(seconds: 5));
} on TimeoutException {
print('请求超时');
}
五、错误处理
捕获常见错误类型:
try {
final response = await http.get(url);
} on http.ClientException catch (e) {
print('客户端错误: $e'); // 如网络不可用
} on SocketException catch (e) {
print('网络连接失败: $e');
} catch (e) {
print('未知错误: $e');
}
六、Dio三方库
0. 安装依赖
dependencies:
dio: ^5.0.0 # 检查最新版本
1. GET 请求
import 'package:dio/dio.dart';
Future<void> fetchData() async {
final dio = Dio();
try {
final response = await dio.get('https://jsonplaceholder.typicode.com/posts/1');
print('GET 响应数据: ${response.data}');
} on DioException catch (e) {
print('GET 请求失败: ${e.message}');
}
}
2. POST 请求
Future<void> postData() async {
final dio = Dio();
final data = {'title': 'foo', 'body': 'bar', 'userId': 1};
try {
final response = await dio.post(
'https://jsonplaceholder.typicode.com/posts',
data: data,
);
print('POST 响应数据: ${response.data}');
} on DioException catch (e) {
print('POST 请求失败: ${e.message}');
}
}
3. PUT 请求
Future<void> updateData() async {
final dio = Dio();
final data = {'title': 'updated title', 'body': 'updated body'};
try {
final response = await dio.put(
'https://jsonplaceholder.typicode.com/posts/1',
data: data,
);
print('PUT 响应数据: ${response.data}');
} on DioException catch (e) {
print('PUT 请求失败: ${e.message}');
}
}
4. DELETE 请求
Future<void> deleteData() async {
final dio = Dio();
try {
final response = await dio.delete('https://jsonplaceholder.typicode.com/posts/1');
print('DELETE 成功: ${response.statusCode}');
} on DioException catch (e) {
print('DELETE 请求失败: ${e.message}');
}
}
5. 文件上传
Future<void> uploadFile() async {
final dio = Dio();
final formData = FormData.fromMap({
'file': await MultipartFile.fromFile('/path/to/file.jpg', filename: 'upload.jpg'),
'key': 'value',
});
try {
final response = await dio.post(
'https://api.example.com/upload',
data: formData,
onSendProgress: (sent, total) {
print('上传进度: ${(sent / total * 100).toStringAsFixed(0)}%');
},
);
print('文件上传成功: ${response.data}');
} on DioException catch (e) {
print('文件上传失败: ${e.message}');
}
}
6. Json 自动解析数据
自定义数据类型解析
Response response = await dio.get(
'/path',
options: Options(responseType: ResponseType.plain),
);
7. 拦截器
dio.interceptors.add(
InterceptorsWrapper(
onRequest: (RequestOptions options, RequestInterceptorHandler handler) {
// 添加统一请求头(如 Token)
options.headers['Authorization'] = 'Bearer token';
return handler.next(options);
},
onResponse: (Response response, ResponseInterceptorHandler handler) {
// 预处理响应数据
return handler.next(response);
},
onError: (DioException error, ErrorInterceptorHandler handler) {
// 统一错误处理(如 Token 过期跳转登录)
if (error.response?.statusCode == 401) {
// 处理逻辑
}
return handler.next(error);
},
),
);
日志拦截器
dio.interceptors.add(LogInterceptor(
request: true,
responseBody: true,
));
8. 配置超时
final dio = Dio(
BaseOptions(
baseUrl: 'https://api.example.com',
connectTimeout: Duration(seconds: 5),
receiveTimeout: Duration(seconds: 3),
headers: {'Content-Type': 'application/json'},
),
);
9. 取消请求
final cancelToken = CancelToken();
// 发送请求时传递 cancelToken
dio.get('/path', cancelToken: cancelToken);
// 取消请求
cancelToken.cancel('用户手动取消');
六、http 与 Dio 的对比
| 特性 | http 包 | Dio |
|---|---|---|
| 依赖体积 | 轻量(无额外依赖) | 较大(支持拦截器等高级功能) |
| JSON 自动解析 | 需手动使用 jsonDecode | 支持自动解析 |
| 拦截器 | 不支持 | 支持请求/响应拦截 |
| 文件上传/下载 | 需手动处理 MultipartRequest | 封装了 FormData 和 download 方法 |
| 超时配置 | 需通过 Future.timeout 实现 | 内置全局超时配置 |
| 取消请求 | 不支持 | 支持 CancelToken |
总结
-
适用场景:
- 使用
http包:适合简单请求、小型项目,或希望减少第三方依赖。 - 使用 Dio:需要拦截器、文件上传/下载、全局配置等高级功能时。
- 使用
-
核心优势:
http包:官方维护,代码简洁,学习成本低。- Dio:功能丰富,适合复杂网络需求。
官方文档: