Appearance
Flutter 网络请求
网络请求概述
在 Flutter 中,网络请求是与后端服务交互的重要方式。Flutter 提供了多种网络请求方案,从基础的 HttpClient 到第三方库如 http、dio 等。
基础网络请求
1. 使用 HttpClient
HttpClient 是 Dart 内置的网络请求类,不需要额外依赖。
示例:
dart
import 'dart:io';
import 'dart:convert';
Future<void> fetchData() async {
try {
// 创建 HttpClient
final httpClient = HttpClient();
// 创建请求
final request = await httpClient.getUrl(Uri.parse('https://api.example.com/data'));
// 发送请求并获取响应
final response = await request.close();
// 读取响应内容
final responseBody = await response.transform(utf8.decoder).join();
// 解析 JSON
final data = jsonDecode(responseBody);
print(data);
// 关闭 HttpClient
httpClient.close();
} catch (e) {
print('Error: $e');
}
}2. 使用 http 包
http 是 Flutter 官方推荐的网络请求包,提供了更简洁的 API。
安装:
bash
flutter pub add http示例:
dart
import 'package:http/http.dart' as http;
import 'dart:convert';
Future<void> fetchData() async {
try {
// 发送 GET 请求
final response = await http.get(Uri.parse('https://api.example.com/data'));
// 检查响应状态
if (response.statusCode == 200) {
// 解析 JSON
final data = jsonDecode(response.body);
print(data);
} else {
print('Request failed with status: ${response.statusCode}');
}
} catch (e) {
print('Error: $e');
}
}
// 发送 POST 请求
Future<void> postData() async {
try {
final response = await http.post(
Uri.parse('https://api.example.com/data'),
headers: <String, String>{
'Content-Type': 'application/json; charset=UTF-8',
},
body: jsonEncode(<String, String>{
'title': 'Flutter',
'body': 'Network request example',
}),
);
if (response.statusCode == 201) {
final data = jsonDecode(response.body);
print(data);
} else {
print('Request failed with status: ${response.statusCode}');
}
} catch (e) {
print('Error: $e');
}
}高级网络请求
1. 使用 dio 包
dio 是一个功能强大的 Dart HTTP 客户端,支持拦截器、全局配置、FormData、文件上传/下载等。
安装:
bash
flutter pub add dio示例:
dart
import 'package:dio/dio.dart';
// 创建 dio 实例
final dio = Dio();
Future<void> fetchData() async {
try {
// 发送 GET 请求
final response = await dio.get('https://api.example.com/data');
print(response.data);
} catch (e) {
print('Error: $e');
}
}
// 发送 POST 请求
Future<void> postData() async {
try {
final response = await dio.post('https://api.example.com/data', data: {
'title': 'Flutter',
'body': 'Dio example',
});
print(response.data);
} catch (e) {
print('Error: $e');
}
}
// 配置 dio
void configureDio() {
// 设置基础 URL
dio.options.baseUrl = 'https://api.example.com';
// 设置超时时间
dio.options.connectTimeout = Duration(seconds: 5);
dio.options.receiveTimeout = Duration(seconds: 3);
// 设置请求头
dio.options.headers['Content-Type'] = 'application/json';
}2. 拦截器
dio 的拦截器可以用于添加认证令牌、处理错误等。
dart
// 添加请求拦截器
dio.interceptors.add(InterceptorsWrapper(
onRequest: (options, handler) {
// 在发送请求前添加认证令牌
options.headers['Authorization'] = 'Bearer token';
return handler.next(options);
},
onResponse: (response, handler) {
// 处理响应
return handler.next(response);
},
onError: (DioError e, handler) {
// 处理错误
if (e.response?.statusCode == 401) {
// 处理未授权错误
print('Unauthorized');
}
return handler.next(e);
},
));网络请求封装
为了代码复用和便于维护,通常会封装网络请求。
1. 创建 API 服务类
dart
import 'package:dio/dio.dart';
class ApiService {
late Dio _dio;
ApiService() {
_dio = Dio();
_configureDio();
}
void _configureDio() {
_dio.options.baseUrl = 'https://api.example.com';
_dio.options.connectTimeout = Duration(seconds: 5);
_dio.options.receiveTimeout = Duration(seconds: 3);
_dio.options.headers['Content-Type'] = 'application/json';
// 添加拦截器
_dio.interceptors.add(InterceptorsWrapper(
onRequest: (options, handler) {
// 添加认证令牌
final token = getToken();
if (token != null) {
options.headers['Authorization'] = 'Bearer $token';
}
return handler.next(options);
},
onError: (DioError e, handler) {
// 统一错误处理
print('API Error: ${e.message}');
return handler.next(e);
},
));
}
Future<dynamic> get(String path, {Map<String, dynamic>? queryParameters}) async {
try {
final response = await _dio.get(path, queryParameters: queryParameters);
return response.data;
} catch (e) {
throw Exception('Failed to get data: $e');
}
}
Future<dynamic> post(String path, {dynamic data}) async {
try {
final response = await _dio.post(path, data: data);
return response.data;
} catch (e) {
throw Exception('Failed to post data: $e');
}
}
Future<dynamic> put(String path, {dynamic data}) async {
try {
final response = await _dio.put(path, data: data);
return response.data;
} catch (e) {
throw Exception('Failed to put data: $e');
}
}
Future<dynamic> delete(String path) async {
try {
final response = await _dio.delete(path);
return response.data;
} catch (e) {
throw Exception('Failed to delete data: $e');
}
}
String? getToken() {
// 从存储中获取令牌
return 'your-token-here';
}
}
// 使用
try {
final apiService = ApiService();
final data = await apiService.get('/users');
print(data);
} catch (e) {
print('Error: $e');
}2. 响应模型
使用 Dart 的 json_serializable 包来自动生成 JSON 序列化代码。
安装:
bash
flutter pub add json_annotation
flutter pub add --dev build_runner json_serializable示例:
dart
import 'package:json_annotation/json_annotation.dart';
part 'user.g.dart';
@JsonSerializable()
class User {
final int id;
final String name;
final String email;
User({required this.id, required this.name, required this.email});
factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);
Map<String, dynamic> toJson() => _$UserToJson(this);
}
// 生成代码
// 运行命令: flutter pub run build_runner build
// 使用
final user = User.fromJson(jsonData);
print(user.name);网络请求状态管理
1. 使用 FutureBuilder
dart
class DataPage extends StatelessWidget {
final ApiService apiService = ApiService();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Data')),
body: FutureBuilder(
future: apiService.get('/users'),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Center(child: CircularProgressIndicator());
} else if (snapshot.hasError) {
return Center(child: Text('Error: ${snapshot.error}'));
} else if (snapshot.hasData) {
final users = snapshot.data as List;
return ListView.builder(
itemCount: users.length,
itemBuilder: (context, index) {
final user = User.fromJson(users[index]);
return ListTile(
title: Text(user.name),
subtitle: Text(user.email),
);
},
);
} else {
return Center(child: Text('No data'));
}
},
),
);
}
}2. 使用 StreamBuilder
dart
class DataPage extends StatefulWidget {
@override
_DataPageState createState() => _DataPageState();
}
class _DataPageState extends State<DataPage> {
final ApiService apiService = ApiService();
late Stream<List<User>> _usersStream;
@override
void initState() {
super.initState();
_usersStream = _fetchUsers();
}
Stream<List<User>> _fetchUsers() async* {
try {
final data = await apiService.get('/users');
final users = (data as List).map((item) => User.fromJson(item)).toList();
yield users;
} catch (e) {
throw Exception('Failed to fetch users');
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Data')),
body: StreamBuilder(
stream: _usersStream,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Center(child: CircularProgressIndicator());
} else if (snapshot.hasError) {
return Center(child: Text('Error: ${snapshot.error}'));
} else if (snapshot.hasData) {
final users = snapshot.data!;
return ListView.builder(
itemCount: users.length,
itemBuilder: (context, index) {
final user = users[index];
return ListTile(
title: Text(user.name),
subtitle: Text(user.email),
);
},
);
} else {
return Center(child: Text('No data'));
}
},
),
);
}
}网络请求最佳实践
- 使用 HTTPS:确保所有网络请求使用 HTTPS 协议
- 添加超时设置:避免请求无限期等待
- 处理错误:合理处理网络错误和服务器错误
- 使用拦截器:统一处理认证、日志等
- 缓存策略:合理使用缓存减少网络请求
- 请求取消:在页面销毁时取消未完成的请求
- 压缩数据:使用 gzip 等压缩方式减少数据传输量
- 批量请求:合并多个请求减少网络开销
- 使用 WebSocket:对于实时数据使用 WebSocket
- 监控网络状态:根据网络状态调整请求策略
总结
Flutter 提供了多种网络请求方案,从基础的 HttpClient 到功能强大的第三方库 dio。选择合适的方案并进行合理的封装,可以使网络请求代码更加简洁、可维护。同时,结合状态管理和错误处理,可以提供更好的用户体验。