Flutter网络请求与状态管理

张开发
2026/4/16 14:36:50 15 分钟阅读

分享文章

Flutter网络请求与状态管理
Flutter网络请求与状态管理1. 前言在现代移动应用开发中网络请求和状态管理是两个核心概念。本文将深入探讨Flutter中的网络请求实现和状态管理方案帮助你创建更加稳定、高效的Flutter应用。2. 网络请求基础2.1 HTTP库选择Flutter中常用的HTTP库httpFlutter官方推荐的基础HTTP库dio功能强大的HTTP客户端支持拦截器、全局配置等chopper基于Retrofit的HTTP客户端生成器2.2 使用http库import package:http/http.dart as http; import dart:convert; FutureMapString, dynamic fetchPost() async { final response await http.get(Uri.parse(https://jsonplaceholder.typicode.com/posts/1)); if (response.statusCode 200) { return jsonDecode(response.body); } else { throw Exception(Failed to load post); } } // 使用 void main() async { try { final post await fetchPost(); print(Title: ${post[title]}); } catch (e) { print(Error: $e); } }2.3 使用dio库import package:dio/dio.dart; final dio Dio(); FutureMapString, dynamic fetchPost() async { final response await dio.get(https://jsonplaceholder.typicode.com/posts/1); return response.data; } // 配置dio void setupDio() { dio.options.baseUrl https://jsonplaceholder.typicode.com; dio.options.connectTimeout 5000; dio.options.receiveTimeout 3000; // 添加拦截器 dio.interceptors.add(InterceptorsWrapper( onRequest: (options, handler) { // 在发送请求前做些什么 print(Requesting: ${options.path}); return handler.next(options); }, onResponse: (response, handler) { // 对响应数据做些什么 print(Response received); return handler.next(response); }, onError: (DioError e, handler) { // 对错误做些什么 print(Error: ${e.message}); return handler.next(e); }, )); }3. 网络请求状态管理3.1 使用FutureBuilderclass PostPage extends StatelessWidget { override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text(Post)), body: FutureBuilderMapString, dynamic( future: fetchPost(), builder: (context, snapshot) { if (snapshot.connectionState ConnectionState.waiting) { return Center(child: CircularProgressIndicator()); } else if (snapshot.hasError) { return Center(child: Text(Error: ${snapshot.error})); } else { final post snapshot.data!; return Padding( padding: EdgeInsets.all(20), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(Title: ${post[title]}, style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold)), SizedBox(height: 20), Text(Body: ${post[body]}), ], ), ); } }, ), ); } }3.2 使用StreamBuilderclass PostBloc { final _controller StreamControllerMapString, dynamic(); StreamMapString, dynamic get postStream _controller.stream; void fetchPost() async { try { final response await http.get(Uri.parse(https://jsonplaceholder.typicode.com/posts/1)); if (response.statusCode 200) { _controller.add(jsonDecode(response.body)); } else { _controller.addError(Failed to load post); } } catch (e) { _controller.addError(e); } } void dispose() { _controller.close(); } } class PostPage extends StatefulWidget { override _PostPageState createState() _PostPageState(); } class _PostPageState extends StatePostPage { final _bloc PostBloc(); override void initState() { super.initState(); _bloc.fetchPost(); } override void dispose() { _bloc.dispose(); super.dispose(); } override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text(Post)), body: StreamBuilderMapString, dynamic( stream: _bloc.postStream, builder: (context, snapshot) { if (snapshot.hasError) { return Center(child: Text(Error: ${snapshot.error})); } else if (!snapshot.hasData) { return Center(child: CircularProgressIndicator()); } else { final post snapshot.data!; return Padding( padding: EdgeInsets.all(20), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(Title: ${post[title]}, style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold)), SizedBox(height: 20), Text(Body: ${post[body]}), ], ), ); } }, ), ); } }3.3 使用Providerclass PostModel extends ChangeNotifier { MapString, dynamic? _post; bool _isLoading false; String? _error; MapString, dynamic? get post _post; bool get isLoading _isLoading; String? get error _error; Futurevoid fetchPost() async { _isLoading true; _error null; notifyListeners(); try { final response await http.get(Uri.parse(https://jsonplaceholder.typicode.com/posts/1)); if (response.statusCode 200) { _post jsonDecode(response.body); } else { _error Failed to load post; } } catch (e) { _error e.toString(); } finally { _isLoading false; notifyListeners(); } } } void main() { runApp( ChangeNotifierProvider( create: (context) PostModel(), child: MyApp(), ), ); } class PostPage extends StatelessWidget { override Widget build(BuildContext context) { final postModel Provider.ofPostModel(context); return Scaffold( appBar: AppBar(title: Text(Post)), body: postModel.isLoading ? Center(child: CircularProgressIndicator()) : postModel.error ! null ? Center(child: Text(Error: ${postModel.error})) : postModel.post ! null ? Padding( padding: EdgeInsets.all(20), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(Title: ${postModel.post![title]}, style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold)), SizedBox(height: 20), Text(Body: ${postModel.post![body]}), ], ), ) : Center(child: Text(No data)), floatingActionButton: FloatingActionButton( onPressed: () postModel.fetchPost(), child: Icon(Icons.refresh), ), ); } }4. 高级网络请求技巧4.1 缓存策略class PostModel extends ChangeNotifier { MapString, dynamic? _post; bool _isLoading false; String? _error; final SharedPreferences _prefs; PostModel(this._prefs) { // 从缓存加载数据 final cachedPost _prefs.getString(post); if (cachedPost ! null) { _post jsonDecode(cachedPost); notifyListeners(); } } MapString, dynamic? get post _post; bool get isLoading _isLoading; String? get error _error; Futurevoid fetchPost() async { _isLoading true; _error null; notifyListeners(); try { final response await http.get(Uri.parse(https://jsonplaceholder.typicode.com/posts/1)); if (response.statusCode 200) { _post jsonDecode(response.body); // 缓存数据 _prefs.setString(post, jsonEncode(_post)); } else { _error Failed to load post; } } catch (e) { _error e.toString(); } finally { _isLoading false; notifyListeners(); } } }4.2 重试机制FutureMapString, dynamic fetchPostWithRetry({int retries 3}) async { int attempt 0; while (attempt retries) { try { final response await http.get(Uri.parse(https://jsonplaceholder.typicode.com/posts/1)); if (response.statusCode 200) { return jsonDecode(response.body); } else { throw Exception(Failed to load post); } } catch (e) { attempt; if (attempt retries) { rethrow; } // 指数退避 await Future.delayed(Duration(milliseconds: 1000 * attempt)); } } throw Exception(Max retries reached); }4.3 并发请求Futurevoid fetchMultiplePosts() async { final futures [ http.get(Uri.parse(https://jsonplaceholder.typicode.com/posts/1)), http.get(Uri.parse(https://jsonplaceholder.typicode.com/posts/2)), http.get(Uri.parse(https://jsonplaceholder.typicode.com/posts/3)), ]; final responses await Future.wait(futures); for (final response in responses) { if (response.statusCode 200) { print(Post: ${jsonDecode(response.body)[title]}); } } }5. 实际应用案例5.1 登录功能class AuthModel extends ChangeNotifier { User? _user; bool _isLoading false; String? _error; User? get user _user; bool get isLoading _isLoading; String? get error _error; bool get isAuthenticated _user ! null; Futurevoid login(String email, String password) async { _isLoading true; _error null; notifyListeners(); try { final response await dio.post(/auth/login, data: { email: email, password: password, }); if (response.statusCode 200) { _user User.fromJson(response.data); // 保存token await _prefs.setString(token, _user!.token); } else { _error Login failed; } } catch (e) { _error e.toString(); } finally { _isLoading false; notifyListeners(); } } Futurevoid logout() async { _user null; await _prefs.remove(token); notifyListeners(); } Futurevoid checkAuth() async { final token _prefs.getString(token); if (token ! null) { try { final response await dio.get(/auth/me, options: Options( headers: {Authorization: Bearer $token}, )); if (response.statusCode 200) { _user User.fromJson(response.data); } } catch (e) { // Token无效清除 await _prefs.remove(token); } finally { notifyListeners(); } } } }5.2 产品列表class ProductModel extends ChangeNotifier { ListProduct _products []; bool _isLoading false; String? _error; ListProduct get products _products; bool get isLoading _isLoading; String? get error _error; Futurevoid fetchProducts() async { _isLoading true; _error null; notifyListeners(); try { final response await dio.get(/products); if (response.statusCode 200) { _products (response.data as List).map((item) Product.fromJson(item)).toList(); } else { _error Failed to load products; } } catch (e) { _error e.toString(); } finally { _isLoading false; notifyListeners(); } } Futurevoid addProduct(Product product) async { _isLoading true; _error null; notifyListeners(); try { final response await dio.post(/products, data: product.toJson()); if (response.statusCode 201) { _products.add(Product.fromJson(response.data)); } else { _error Failed to add product; } } catch (e) { _error e.toString(); } finally { _isLoading false; notifyListeners(); } } Futurevoid updateProduct(Product product) async { _isLoading true; _error null; notifyListeners(); try { final response await dio.put(/products/${product.id}, data: product.toJson()); if (response.statusCode 200) { final index _products.indexWhere((p) p.id product.id); if (index 0) { _products[index] Product.fromJson(response.data); } } else { _error Failed to update product; } } catch (e) { _error e.toString(); } finally { _isLoading false; notifyListeners(); } } Futurevoid deleteProduct(int id) async { _isLoading true; _error null; notifyListeners(); try { final response await dio.delete(/products/$id); if (response.statusCode 200) { _products.removeWhere((p) p.id id); } else { _error Failed to delete product; } } catch (e) { _error e.toString(); } finally { _isLoading false; notifyListeners(); } } }6. 性能优化6.1 网络请求优化使用缓存缓存频繁访问的数据压缩数据使用gzip压缩传输数据批量请求合并多个请求为一个使用WebSocket对于实时数据使用WebSocket6.2 状态管理优化避免过度重建使用Selector或listen: false批量更新合并多个状态更新使用防抖对于频繁触发的请求使用防抖6.3 错误处理统一错误处理创建统一的错误处理机制用户友好的错误提示提供清晰、友好的错误信息网络状态检测检测网络状态并提供相应的提示7. 总结Flutter中的网络请求和状态管理是构建现代移动应用的核心部分。通过本文的介绍你应该对Flutter的网络请求实现和状态管理方案有了更深入的了解包括HTTP库选择、网络请求状态管理、高级网络请求技巧、实际应用案例以及性能优化等内容。选择合适的HTTP库和状态管理方案对于应用的性能和可维护性至关重要。希望本文对你有所帮助祝你在Flutter开发的道路上取得成功

更多文章