当前位置: 首页 > news >正文

Flutter 网络请求高级技巧完全指南

Flutter 网络请求高级技巧完全指南

引言

网络请求是移动应用开发中不可或缺的一部分。Flutter 提供了多种网络请求方案,本文将深入探讨网络请求的高级技巧,包括请求封装、错误处理、缓存策略等内容。

网络请求基础回顾

Flutter 中常用的网络请求库有:

  1. http- Flutter 官方提供的基础 HTTP 库
  2. dio- 强大的 HTTP 客户端,支持拦截器、请求取消等
  3. retrofit- 类型安全的 HTTP 客户端
// 使用 http 库 import 'package:http/http.dart' as http; final response = await http.get(Uri.parse('https://api.example.com/data'));

高级技巧一:请求封装

创建网络服务类

import 'package:dio/dio.dart'; class ApiService { static final Dio _dio = Dio(BaseOptions( baseUrl: 'https://api.example.com', connectTimeout: const Duration(seconds: 10), receiveTimeout: const Duration(seconds: 10), )); static Future<Response<T>> get<T>( String path, { Map<String, dynamic>? queryParameters, Options? options, }) async { try { final response = await _dio.get<T>( path, queryParameters: queryParameters, options: options, ); return response; } catch (e) { throw _handleError(e); } } static Future<Response<T>> post<T>( String path, { dynamic data, Map<String, dynamic>? queryParameters, Options? options, }) async { try { final response = await _dio.post<T>( path, data: data, queryParameters: queryParameters, options: options, ); return response; } catch (e) { throw _handleError(e); } } static Exception _handleError(dynamic e) { if (e is DioException) { switch (e.type) { case DioExceptionType.connectionTimeout: return Exception('连接超时'); case DioExceptionType.sendTimeout: return Exception('发送超时'); case DioExceptionType.receiveTimeout: return Exception('接收超时'); case DioExceptionType.badResponse: return Exception('服务器错误: ${e.response?.statusCode}'); case DioExceptionType.cancel: return Exception('请求已取消'); default: return Exception('网络错误: ${e.message}'); } } return Exception('未知错误'); } }

高级技巧二:拦截器配置

请求拦截器

class AuthInterceptor extends Interceptor { @override void onRequest(RequestOptions options, RequestInterceptorHandler handler) { // 添加 token final token = getToken(); if (token != null) { options.headers['Authorization'] = 'Bearer $token'; } // 添加通用参数 options.queryParameters['platform'] = 'flutter'; options.queryParameters['version'] = '1.0.0'; super.onRequest(options, handler); } } class LogInterceptor extends Interceptor { @override void onRequest(RequestOptions options, RequestInterceptorHandler handler) { print('请求方法: ${options.method}'); print('请求地址: ${options.uri}'); print('请求参数: ${options.data}'); super.onRequest(options, handler); } @override void onResponse(Response response, ResponseInterceptorHandler handler) { print('响应状态: ${response.statusCode}'); print('响应数据: ${response.data}'); super.onResponse(response, handler); } @override void onError(DioException err, ErrorInterceptorHandler handler) { print('错误信息: ${err.message}'); super.onError(err, handler); } } // 注册拦截器 _dio.interceptors.add(AuthInterceptor()); _dio.interceptors.add(LogInterceptor());

高级技巧三:请求取消

单个请求取消

CancelToken cancelToken = CancelToken(); try { final response = await _dio.get( '/api/data', cancelToken: cancelToken, ); } on DioException catch (e) { if (DioException.cancel == e.type) { print('请求已取消'); } } // 取消请求 cancelToken.cancel('用户主动取消');

批量请求取消

class CancelTokenManager { final Map<String, CancelToken> _tokens = {}; void addToken(String key, CancelToken token) { _tokens[key] = token; } void cancelToken(String key) { _tokens[key]?.cancel(); _tokens.remove(key); } void cancelAll() { _tokens.forEach((key, token) => token.cancel()); _tokens.clear(); } CancelToken getToken(String key) { return _tokens[key] ?? CancelToken(); } }

高级技巧四:响应数据解析

泛型解析

class ApiResponse<T> { final int code; final String message; final T? data; ApiResponse({ required this.code, required this.message, this.data, }); factory ApiResponse.fromJson(Map<String, dynamic> json, T Function(dynamic) fromJson) { return ApiResponse<T>( code: json['code'] as int, message: json['message'] as String, data: json['data'] != null ? fromJson(json['data']) : null, ); } } // 使用 final response = await ApiService.get<Map<String, dynamic>>('/api/user'); final apiResponse = ApiResponse<User>.fromJson( response.data!, (json) => User.fromJson(json as Map<String, dynamic>), );

列表数据解析

final response = await ApiService.get<Map<String, dynamic>>('/api/users'); final apiResponse = ApiResponse<List<User>>.fromJson( response.data!, (json) => (json as List).map((e) => User.fromJson(e as Map<String, dynamic>)).toList(), );

高级技巧五:缓存策略

内存缓存

class MemoryCache { static final Map<String, dynamic> _cache = {}; static final Map<String, DateTime> _cacheTime = {}; static const Duration _expireDuration = Duration(minutes: 5); static T? get<T>(String key) { if (!_cache.containsKey(key)) return null; final expireTime = _cacheTime[key]; if (expireTime != null && DateTime.now().isAfter(expireTime)) { _cache.remove(key); _cacheTime.remove(key); return null; } return _cache[key] as T?; } static void set<T>(String key, T value) { _cache[key] = value; _cacheTime[key] = DateTime.now().add(_expireDuration); } static void remove(String key) { _cache.remove(key); _cacheTime.remove(key); } static void clear() { _cache.clear(); _cacheTime.clear(); } }

网络优先缓存策略

Future<T> fetchWithCache<T>( String key, Future<T> Function() fetchFunction, { Duration expireDuration = const Duration(minutes: 5), }) async { // 先尝试获取缓存 final cachedData = MemoryCache.get<T>(key); if (cachedData != null) { return cachedData; } // 缓存不存在或过期,发起网络请求 final data = await fetchFunction(); // 缓存数据 MemoryCache.set<T>(key, data); return data; }

高级技巧六:请求重试

自动重试机制

class RetryInterceptor extends Interceptor { final int maxRetries; final List<int> retryStatusCodes; RetryInterceptor({ this.maxRetries = 3, this.retryStatusCodes = const [500, 502, 503, 504], }); @override void onError(DioException err, ErrorInterceptorHandler handler) { if (err.response != null && retryStatusCodes.contains(err.response!.statusCode) && _shouldRetry(err)) { _retryRequest(err, handler); return; } super.onError(err, handler); } bool _shouldRetry(DioException err) { return err.requestOptions.extra['retryCount'] != null && err.requestOptions.extra['retryCount'] < maxRetries; } void _retryRequest(DioException err, ErrorInterceptorHandler handler) async { final currentRetry = err.requestOptions.extra['retryCount'] ?? 0; final nextRetry = currentRetry + 1; // 延迟重试 await Future.delayed(Duration(seconds: nextRetry * 2)); // 更新重试次数 err.requestOptions.extra['retryCount'] = nextRetry; try { final response = await _dio.fetch(err.requestOptions); handler.resolve(response); } catch (e) { handler.reject(e as DioException); } } }

高级技巧七:进度监听

上传进度

final response = await _dio.post( '/api/upload', data: MultipartFile.fromFileSync( 'file_path', filename: 'test.png', ), onSendProgress: (int sent, int total) { final progress = (sent / total) * 100; print('上传进度: ${progress.toStringAsFixed(2)}%'); }, );

下载进度

await _dio.download( 'https://example.com/file.zip', '/path/to/save/file.zip', onReceiveProgress: (int received, int total) { final progress = (received / total) * 100; print('下载进度: ${progress.toStringAsFixed(2)}%'); }, );

实战案例:完整的网络请求服务

class UserService { static const String _basePath = '/api/users'; static Future<User> getUser(int id) async { final response = await ApiService.get<Map<String, dynamic>>('$_basePath/$id'); return User.fromJson(response.data!); } static Future<List<User>> getUsers({int page = 1, int limit = 10}) async { final response = await ApiService.get<Map<String, dynamic>>( _basePath, queryParameters: {'page': page, 'limit': limit}, ); final data = response.data!['data'] as List; return data.map((e) => User.fromJson(e as Map<String, dynamic>)).toList(); } static Future<User> createUser(User user) async { final response = await ApiService.post<Map<String, dynamic>>( _basePath, data: user.toJson(), ); return User.fromJson(response.data!); } static Future<User> updateUser(int id, User user) async { final response = await ApiService.put<Map<String, dynamic>>( '$_basePath/$id', data: user.toJson(), ); return User.fromJson(response.data!); } static Future<void> deleteUser(int id) async { await ApiService.delete('$_basePath/$id'); } } class User { final int id; final String name; final String email; final String avatar; User({ required this.id, required this.name, required this.email, required this.avatar, }); factory User.fromJson(Map<String, dynamic> json) { return User( id: json['id'] as int, name: json['name'] as String, email: json['email'] as String, avatar: json['avatar'] as String, ); } Map<String, dynamic> toJson() { return { 'id': id, 'name': name, 'email': email, 'avatar': avatar, }; } }

实战案例:网络状态管理

import 'package:flutter_bloc/flutter_bloc.dart'; abstract class UserState {} class UserInitial extends UserState {} class UserLoading extends UserState {} class UserLoaded extends UserState { final List<User> users; UserLoaded({required this.users}); } class UserError extends UserState { final String message; UserError({required this.message}); } class UserCubit extends Cubit<UserState> { UserCubit() : super(UserInitial()); Future<void> fetchUsers() async { emit(UserLoading()); try { final users = await UserService.getUsers(); emit(UserLoaded(users: users)); } catch (e) { emit(UserError(message: e.toString())); } } }

常见问题与解决方案

Q1:如何处理 SSL 证书问题?

A:在 Dio 中配置证书:

Dio dio = Dio(); dio.httpClientAdapter = Http2Adapter( ConnectionManager( onClientCreate: (_, config) => config.onBadCertificate = (_) => true, ), );

Q2:如何设置代理?

A:在 Dio 的 BaseOptions 中配置:

BaseOptions options = BaseOptions( proxy: 'http://proxy.example.com:8080', ); Dio dio = Dio(options);

Q3:如何处理 Cookie?

A:使用 cookie_jar 包:

import 'package:dio_cookie_manager/dio_cookie_manager.dart'; import 'package:cookie_jar/cookie_jar.dart'; final cookieJar = CookieJar(); _dio.interceptors.add(CookieManager(cookieJar));

性能优化技巧

  1. 使用连接池:复用 HTTP 连接
  2. 设置合理的超时时间:避免长时间等待
  3. 启用 gzip 压缩:减少数据传输量
  4. 缓存策略:减少重复请求
  5. 请求合并:合并多个相似请求

总结

网络请求是 Flutter 应用开发的核心功能之一。通过本文的学习,你应该能够:

  1. 创建封装良好的网络服务类
  2. 使用拦截器处理请求和响应
  3. 实现请求取消和重试机制
  4. 配置缓存策略
  5. 监听上传/下载进度

掌握这些高级技巧,能够帮助你构建更加稳定、高效的网络请求系统。

http://www.jsqmd.com/news/794635/

相关文章:

  • 2026年|还在为AIGC疑似率高彻夜难眠?亲测5款降AI率工具,教你高效通过AI检测!建议收藏 - 降AI实验室
  • 直播场景智能告警系统设计:从告警风暴到精准可操作通知
  • 从电话语音到网络传输:手把手教你用C语言实现PCM与G.711(a-law/u-law)的互转
  • FakeLocation终极教程:三分钟掌握Android虚拟定位黑科技
  • 词源探秘|从orient到panorama:解码英语单词背后的文明密码
  • Simulink - 从理论到实践:Coulomb and Viscous Friction模块的建模精要与避坑指南
  • 告别ENVI/Erdas!用PCI Geomatica Banff版搞定Pleiades立体像对DEM提取(附详细流程与踩坑记录)
  • 自动化计算机架构探索:后摩尔时代的性能突破
  • 告别软件模拟!用STM32CubeMX HAL库硬件IIC驱动AT24C02,实测避坑与性能对比
  • 静态页面构建优化:从核心技能到自动化部署实践
  • Flutter × Harmony6.0 打造高颜值优惠商城页面:跨端 UI 构建与组件化实践
  • 基于MCP协议与Playwright的AI智能体网页抓取工具部署与实战
  • 网盘直链下载助手:九大网盘免费获取真实下载链接的完整解决方案
  • BepInEx 6.0.0架构升级:如何根治IL2CPP签名耗尽与资源管理崩溃?
  • ViGEmBus虚拟游戏控制器驱动终极指南:Windows内核级游戏手柄模拟深度解析
  • 3个技巧彻底改变你的泰坦之旅装备管理体验
  • 从选股到复盘:我用 AI Agent 跑了一套股票辅助系统 - Leone
  • STM32F103点灯实战:手把手教你用CLion配置OpenOCD与JLink双调试通道(附DSP库添加技巧)
  • 后量子密码学硬件加速器的NTT侧信道防护分析
  • Arm GIC虚拟中断控制器架构与寄存器详解
  • 3分钟解锁碧蓝航线全皮肤:Perseus补丁新手完全指南
  • 解读重庆旧房翻新设计要点,如何选择一家靠谱的旧房翻新改造公司 - 大渝测评
  • 图数据库如何为AI代理构建持久化记忆系统:FalkorDB与Mem0实践
  • 2048 AI助手:如何让算法帮你轻松突破2048极限?
  • 锂离子与磷酸铁锂电池技术对比及汽车安全应用
  • 3分钟让Windows任务栏焕然一新:TranslucentTB场景化配置全攻略
  • GetQzonehistory:三步快速备份你的QQ空间历史说说,永久保存青春记忆
  • 3个秘籍解锁百度网盘提取码:告别繁琐搜索的智能解决方案
  • 如何通过LizzieYzy围棋AI分析平台实现棋力跨越式提升?
  • 别再被证书格式搞晕了!保姆级图解:.pem、.crt、.pfx到底怎么选、怎么转?