Flutter中FutureProvider取数据到GridView遇连接拒绝错误及优化咨询
「Connection refused」错误排查
先从这几个核心方向排查问题:
- API地址与网络可达性:
- 若后端是本地服务,Android模拟器不能直接用
localhost,要替换成10.0.2.2;真机需和后端在同一局域网,用电脑的局域网IP(比如192.168.1.100)。 - 确认后端服务正在运行,端口没被防火墙拦截,先用浏览器或Postman测试目标URL能否正常返回数据。
- 若后端是本地服务,Android模拟器不能直接用
- 项目网络权限:
- Android:在
android/app/src/main/AndroidManifest.xml里添加<uses-permission android:name="android.permission.INTERNET"/>。 - iOS:如果用的是HTTP请求,要在
Info.plist中配置NSAppTransportSecurity,允许非HTTPS请求。
- Android:在
- 请求代码细节:
- 检查请求方法(GET/POST)、请求头是否符合API要求,比如API要求
Content-Type: application/json但代码里没添加。 - 举个常见的仓库代码错误示例:
把class FakeProductRepository { Future<List<Product>> getProducts() async { // 错误:模拟器无法访问localhost final response = await http.get(Uri.parse('http://localhost:3000/products')); if (response.statusCode == 200) { final data = jsonDecode(response.body)['data'] as List; return data.map((e) => Product.fromJson(e)).toList(); } else { throw Exception('Failed to load products'); } } }localhost换成10.0.2.2(模拟器)或局域网IP即可解决。
- 检查请求方法(GET/POST)、请求头是否符合API要求,比如API要求
多请求场景下的Provider封装优化
不用每个请求都单独创建FutureProvider,推荐这几种更高效的封装方案:
1. 用Family实现参数化请求
如果请求需要传入动态参数(比如按分类ID获取商品),用FutureProvider.family复用请求逻辑:
// 定义带参数的Provider final categoryProductsProvider = FutureProvider.family<List<Product>, int>((ref, categoryId) { return ref.watch(productRepositoryProvider).getProductsByCategory(categoryId); }); // 组件中使用时传入参数 ref.watch(categoryProductsProvider(1)); // 获取分类ID为1的商品列表
2. 基于Repository封装,配合AsyncValue管理多请求
把所有请求逻辑集中在Repository里,通过单个Provider提供实例,组件中直接调用方法并结合AsyncValue统一处理状态:
// 提供Repository实例 final productRepositoryProvider = Provider<FakeProductRepository>((ref) { return FakeProductRepository(); }); // 组件中调用多个请求 final repo = ref.watch(productRepositoryProvider); // 用AsyncValue统一处理加载/错误/数据状态 final allProductsAsync = AsyncValue.guard(() => repo.getProducts()); final hotProductsAsync = AsyncValue.guard(() => repo.getHotProducts());
3. 用StateNotifier管理多请求状态(复杂场景首选)
如果需要缓存数据、处理多个请求的状态联动,用StateNotifier封装所有请求状态:
// 定义状态类,包含多个请求的状态 class ProductState extends Equatable { final AsyncValue<List<Product>> allProducts; final AsyncValue<List<Product>> hotProducts; const ProductState({ this.allProducts = const AsyncValue.loading(), this.hotProducts = const AsyncValue.loading(), }); ProductState copyWith({ AsyncValue<List<Product>>? allProducts, AsyncValue<List<Product>>? hotProducts, }) => ProductState( allProducts: allProducts ?? this.allProducts, hotProducts: hotProducts ?? this.hotProducts, ); @override List<Object?> get props => [allProducts, hotProducts]; } // 状态管理器 class ProductNotifier extends StateNotifier<ProductState> { final FakeProductRepository _repo; ProductNotifier(this._repo) : super(const ProductState()); // 获取全部商品 Future<void> fetchAllProducts() async { state = state.copyWith(allProducts: const AsyncValue.loading()); try { final products = await _repo.getProducts(); state = state.copyWith(allProducts: AsyncValue.data(products)); } catch (e, stack) { state = state.copyWith(allProducts: AsyncValue.error(e, stack)); } } // 获取热门商品 Future<void> fetchHotProducts() async { state = state.copyWith(hotProducts: const AsyncValue.loading()); try { final products = await _repo.getHotProducts(); state = state.copyWith(hotProducts: AsyncValue.data(products)); } catch (e, stack) { state = state.copyWith(hotProducts: AsyncValue.error(e, stack)); } } } // 定义Provider final productNotifierProvider = StateNotifierProvider<ProductNotifier, ProductState>((ref) { final repo = ref.watch(productRepositoryProvider); final notifier = ProductNotifier(repo); notifier.fetchAllProducts(); // 初始化加载数据 return notifier; }); // 组件中使用 final productState = ref.watch(productNotifierProvider); // 渲染全部商品列表 productState.allProducts.when( loading: () => const CircularProgressIndicator(), error: (e, _) => Text('加载失败:$e'), data: (products) => GridView.builder( itemCount: products.length, gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 2), itemBuilder: (context, index) => ProductCard(product: products[index]), ), );
4. AsyncNotifier(Riverpod 2.0+ 简洁方案)
Riverpod 2.0的AsyncNotifier可以更简洁地处理异步状态,同时支持单个和额外请求:
final allProductsProvider = AsyncNotifierProvider<ProductsNotifier, List<Product>>(() { return ProductsNotifier(); }); class ProductsNotifier extends AsyncNotifier<List<Product>> { late final FakeProductRepository _repo; @override Future<List<Product>> build() async { _repo = ref.watch(productRepositoryProvider); return _repo.getProducts(); // 初始化加载数据 } // 刷新全部商品 Future<void> refresh() async { state = const AsyncValue.loading(); state = AsyncValue.data(await _repo.getProducts()); } // 获取分类商品(额外请求) Future<List<Product>> getCategoryProducts(int categoryId) async { return _repo.getProductsByCategory(categoryId); } } // 组件中使用 final allProducts = ref.watch(allProductsProvider); // 调用额外请求 final categoryProducts = await ref.read(allProductsProvider.notifier).getCategoryProducts(1);
内容的提问来源于stack exchange,提问作者Mohammed M
相关产品推荐
相关产品推荐

