Flutter 网络请求与数据处理:从基础到单例封装
在 Flutter 开发中,网络请求是一个非常常见的需求,比如获取 API 数据、上传文件、处理分页加载等。为了高效地处理网络请求和数据管理,我们需要选择合适的工具并进行合理的封装。
本篇博客将详细介绍 Flutter 中网络请求的基础知识、常用库(如 http
和 dio
),并最终实现一个单例模式的网络请求封装,帮助你在项目中高效管理网络请求。
1. 网络请求的基础知识
1.1 HTTP 请求的基本类型
- GET:从服务器获取数据。
- POST:向服务器发送数据。
- PUT:更新服务器上的数据。
- DELETE:删除服务器上的数据。
1.2 常用的网络请求库
http
:- Flutter 官方提供的轻量级 HTTP 客户端。
- 适合简单的网络请求。
dio
:- 功能强大的第三方库,支持拦截器、文件上传下载、请求取消等。
- 适合复杂的网络请求场景。
2. 使用 http
进行网络请求
2.1 安装 http
在 pubspec.yaml
中添加依赖:
dependencies:
http: ^0.15.0
运行以下命令安装依赖:
flutter pub get
2.2 基本用法
GET 请求
import 'dart:convert';
import 'package:http/http.dart' as http;
void fetchData() async {
final url = Uri.parse("https://jsonplaceholder.typicode.com/posts");
final response = await http.get(url);
if (response.statusCode == 200) {
final data = json.decode(response.body);
print("数据加载成功:$data");
} else {
print("请求失败,状态码:${response.statusCode}");
}
}
void main() {
fetchData();
}
POST 请求
void postData() async {
final url = Uri.parse("https://jsonplaceholder.typicode.com/posts");
final response = await http.post(
url,
headers: {"Content-Type": "application/json"},
body: json.encode({"title": "Flutter", "body": "Hello World", "userId": 1}),
);
if (response.statusCode == 201) {
final data = json.decode(response.body);
print("数据提交成功:$data");
} else {
print("请求失败,状态码:${response.statusCode}");
}
}
void main() {
postData();
}
3. 使用 dio
进行网络请求
3.1 安装 dio
在 pubspec.yaml
中添加依赖:
dependencies:
dio: ^5.0.0
运行以下命令安装依赖:
flutter pub get
3.2 基本用法
GET 请求
import 'package:dio/dio.dart';
void fetchData() async {
final dio = Dio();
final response = await dio.get("https://jsonplaceholder.typicode.com/posts");
if (response.statusCode == 200) {
print("数据加载成功:${response.data}");
} else {
print("请求失败,状态码:${response.statusCode}");
}
}
void main() {
fetchData();
}
POST 请求
void postData() async {
final dio = Dio();
final response = await dio.post(
"https://jsonplaceholder.typicode.com/posts",
data: {"title": "Flutter", "body": "Hello World", "userId": 1},
);
if (response.statusCode == 201) {
print("数据提交成功:${response.data}");
} else {
print("请求失败,状态码:${response.statusCode}");
}
}
void main() {
postData();
}
3.3 使用拦截器
dio
提供了强大的拦截器功能,可以在请求前后进行统一处理。
示例:添加拦截器
void fetchDataWithInterceptor() async {
final dio = Dio();
// 添加拦截器
dio.interceptors.add(InterceptorsWrapper(
onRequest: (options, handler) {
print("请求开始:${options.uri}");
return handler.next(options);
},
onResponse: (response, handler) {
print("请求成功:${response.data}");
return handler.next(response);
},
onError: (error, handler) {
print("请求失败:${error.message}");
return handler.next(error);
},
));
final response = await dio.get("https://jsonplaceholder.typicode.com/posts");
print(response.data);
}
void main() {
fetchDataWithInterceptor();
}
4. 单例模式封装网络请求
在实际项目中,网络请求通常需要统一管理,比如设置基础 URL、添加拦截器、处理错误等。通过单例模式封装网络请求,可以提高代码的复用性和可维护性。
4.1 单例封装 dio
封装代码
import 'package:dio/dio.dart';
class DioClient {
// 单例模式
static final DioClient _instance = DioClient._internal();
factory DioClient() => _instance;
late Dio _dio;
DioClient._internal() {
_dio = Dio(BaseOptions(
baseUrl: "https://jsonplaceholder.typicode.com",
connectTimeout: const Duration(seconds: 10),
receiveTimeout: const Duration(seconds: 10),
headers: {"Content-Type": "application/json"},
));
// 添加拦截器
_dio.interceptors.add(InterceptorsWrapper(
onRequest: (options, handler) {
print("请求开始:${options.uri}");
return handler.next(options);
},
onResponse: (response, handler) {
print("请求成功:${response.data}");
return handler.next(response);
},
onError: (error, handler) {
print("请求失败:${error.message}");
return handler.next(error);
},
));
}
// GET 请求
Future<Response> get(String path, {Map<String, dynamic>? queryParameters}) async {
return await _dio.get(path, queryParameters: queryParameters);
}
// POST 请求
Future<Response> post(String path, {Map<String, dynamic>? data}) async {
return await _dio.post(path, data: data);
}
// PUT 请求
Future<Response> put(String path, {Map<String, dynamic>? data}) async {
return await _dio.put(path, data: data);
}
// DELETE 请求
Future<Response> delete(String path, {Map<String, dynamic>? data}) async {
return await _dio.delete(path, data: data);
}
}
4.2 使用封装的 DioClient
GET 请求
void fetchData() async {
final dioClient = DioClient();
final response = await dioClient.get("/posts");
if (response.statusCode == 200) {
print("数据加载成功:${response.data}");
} else {
print("请求失败,状态码:${response.statusCode}");
}
}
void main() {
fetchData();
}
POST 请求
void postData() async {
final dioClient = DioClient();
final response = await dioClient.post(
"/posts",
data: {"title": "Flutter", "body": "Hello World", "userId": 1},
);
if (response.statusCode == 201) {
print("数据提交成功:${response.data}");
} else {
print("请求失败,状态码:${response.statusCode}");
}
}
void main() {
postData();
}
5. 总结
5.1 http
和 dio
的对比
特性 | http | dio |
---|---|---|
功能 | 轻量级,适合简单请求 | 功能强大,支持拦截器、文件上传等 |
学习曲线 | 低 | 中 |
扩展性 | 较低 | 高 |
适用场景 | 小型项目 | 中型和大型项目 |
5.2 实践建议
- 小型项目:
- 使用
http
,简单易用。
- 使用
- 中型和大型项目:
- 使用
dio
,并通过单例模式封装,统一管理网络请求。
- 使用