引言
网络请求是现代应用的核心功能之一。Flutter提供了多种方式来处理网络请求,从原生的http包到功能强大的dio库。本文将深入探讨Flutter网络请求的最佳实践,帮助你构建健壮的网络层。
一、网络请求基础
1.1 选择合适的库
| 库 | 特点 | 适用场景 |
|---|---|---|
| http | 官方包,轻量级 | 简单请求 |
| dio | 功能强大,支持拦截器 | 复杂场景 |
| retrofit | 类型安全,代码生成 | 大型项目 |
1.2 基本HTTP请求
import 'package:http/http.dart' as http; Future<void> fetchData() async { final response = await http.get( Uri.parse('https://api.example.com/data'), headers: {'Authorization': 'Bearer token'}, ); if (response.statusCode == 200) { print(response.body); } else { throw Exception('请求失败'); } }1.3 POST请求
Future<void> postData() async { final response = await http.post( Uri.parse('https://api.example.com/data'), headers: {'Content-Type': 'application/json'}, body: jsonEncode({'name': 'John', 'age': 30}), ); }二、dio库详解
2.1 安装与配置
dependencies: dio: ^5.0.0import 'package:dio/dio.dart'; final dio = Dio();2.2 基本配置
final dio = Dio( BaseOptions( baseUrl: 'https://api.example.com', connectTimeout: const Duration(seconds: 5), receiveTimeout: const Duration(seconds: 3), headers: { 'Content-Type': 'application/json', }, ), );2.3 GET请求
Future<User> getUser(String id) async { try { final response = await dio.get('/users/$id'); return User.fromJson(response.data); } catch (e) { throw Exception('获取用户失败: $e'); } }2.4 POST请求
Future<User> createUser(User user) async { final response = await dio.post( '/users', data: user.toJson(), ); return User.fromJson(response.data); }三、拦截器
3.1 请求拦截器
dio.interceptors.add(InterceptorsWrapper( onRequest: (options, handler) { options.headers['Authorization'] = 'Bearer ${AuthService.token}'; return handler.next(options); }, ));3.2 响应拦截器
dio.interceptors.add(InterceptorsWrapper( onResponse: (response, handler) { if (response.statusCode == 401) { AuthService.refreshToken(); } return handler.next(response); }, ));3.3 错误拦截器
dio.interceptors.add(InterceptorsWrapper( onError: (error, handler) { if (error.response?.statusCode == 500) { showError('服务器错误'); } return handler.next(error); }, ));3.4 日志拦截器
dio.interceptors.add(LogInterceptor( requestBody: true, responseBody: true, ));四、错误处理
4.1 统一错误处理
class ApiException implements Exception { final String message; final int? statusCode; ApiException(this.message, {this.statusCode}); @override String toString() => 'ApiException: $message'; } Future<T> safeApiCall<T>(Future<T> Function() apiCall) async { try { return await apiCall(); } on DioError catch (e) { throw ApiException( e.response?.data['message'] ?? '网络请求失败', statusCode: e.response?.statusCode, ); } catch (e) { throw ApiException(e.toString()); } }4.2 重试机制
dio.interceptors.add(RetryInterceptor( retries: 3, retryDelay: const Duration(seconds: 1), retryEvaluator: (error) => error.response?.statusCode == 500, ));五、请求取消
5.1 取消单个请求
CancelToken cancelToken = CancelToken(); dio.get('/data', cancelToken: cancelToken); // 取消请求 cancelToken.cancel('请求已取消');5.2 取消多个请求
final cancelToken = CancelToken(); // 多个请求使用同一个cancelToken dio.get('/data1', cancelToken: cancelToken); dio.get('/data2', cancelToken: cancelToken); // 取消所有请求 cancelToken.cancel('全部取消');六、文件上传下载
6.1 文件上传
Future<void> uploadFile(File file) async { final formData = FormData.fromMap({ 'file': await MultipartFile.fromFile(file.path), 'name': 'test.jpg', }); await dio.post('/upload', data: formData); }6.2 文件下载
Future<void> downloadFile(String url, String savePath) async { await dio.download( url, savePath, onReceiveProgress: (received, total) { final progress = (received / total * 100).toStringAsFixed(0); print('下载进度: $progress%'); }, ); }七、实战案例:API服务
7.1 创建API服务类
class ApiService { final Dio _dio; ApiService(this._dio); Future<User> getUser(String id) async { final response = await _dio.get('/users/$id'); return User.fromJson(response.data); } Future<List<User>> getUsers() async { final response = await _dio.get('/users'); return (response.data as List).map((e) => User.fromJson(e)).toList(); } Future<User> createUser(User user) async { final response = await _dio.post('/users', data: user.toJson()); return User.fromJson(response.data); } Future<void> updateUser(String id, User user) async { await _dio.put('/users/$id', data: user.toJson()); } Future<void> deleteUser(String id) async { await _dio.delete('/users/$id'); } }7.2 依赖注入
// 使用GetIt final getIt = GetIt.instance; void setupLocator() { getIt.registerSingleton<Dio>(Dio( BaseOptions(baseUrl: 'https://api.example.com'), )); getIt.registerSingleton<ApiService>(ApiService(getIt())); }7.3 配合状态管理
class UserViewModel extends ChangeNotifier { final ApiService _apiService; User? _user; bool _isLoading = false; String? _error; UserViewModel(this._apiService); Future<void> loadUser(String id) async { _isLoading = true; _error = null; notifyListeners(); try { _user = await _apiService.getUser(id); } catch (e) { _error = e.toString(); } _isLoading = false; notifyListeners(); } }八、性能优化
8.1 请求缓存
final cache = <String, dynamic>{}; Future<T> cachedRequest<T>(String key, Future<T> Function() request) async { if (cache.containsKey(key)) { return cache[key] as T; } final result = await request(); cache[key] = result; return result; }8.2 请求合并
Future<List<User>> fetchUsers(List<String> ids) async { final futures = ids.map((id) => _apiService.getUser(id)); return await Future.wait(futures); }8.3 连接池
final dio = Dio(BaseOptions( connectTimeout: const Duration(seconds: 5), receiveTimeout: const Duration(seconds: 3), maxConnectionsPerHost: 5, ));九、最佳实践
9.1 环境配置
enum Environment { development, staging, production } class Config { static String baseUrl(Environment env) { switch (env) { case Environment.development: return 'https://dev.api.example.com'; case Environment.staging: return 'https://staging.api.example.com'; case Environment.production: return 'https://api.example.com'; } } }9.2 日志记录
dio.interceptors.add(InterceptorsWrapper( onRequest: (options, handler) { print('请求: ${options.method} ${options.path}'); return handler.next(options); }, ));9.3 测试网络层
void main() { test('getUser returns user', () async { final dio = Dio(BaseOptions(baseUrl: 'https://api.example.com')); final service = ApiService(dio); final user = await service.getUser('1'); expect(user.id, '1'); }); }十、总结
Flutter网络请求是应用开发的核心技能,通过合理使用dio库和良好的架构设计,可以构建健壮的网络层。
关键要点:
- 使用dio进行网络请求
- 使用拦截器处理请求/响应
- 实现统一错误处理
- 支持请求取消和重试
- 考虑性能优化和缓存
掌握网络请求,将使你的Flutter应用更加稳定和高效。