Flutter/Riverpod:如何优化FutureProvider的更新逻辑?
优化方案
1. 移除不必要的setState
原代码里用setState包裹ref.invalidate完全多余,Riverpod的ref操作本身会触发Provider重新计算,进而更新依赖该Provider的Widget。直接修改按钮点击逻辑即可:
onPressed: () { ref.invalidate(myFutureProvider); },
2. 自动监听网络状态变化(替代手动重试)
把网络状态检测封装为独立的StreamProvider,让myFutureProvider依赖它,这样网络状态变化时会自动触发数据重新加载,无需手动点击按钮:
第一步:创建网络状态监听Provider
final connectivityProvider = StreamProvider<ConnectivityResult>((ref) { final connectivity = Connectivity(); // 先获取当前网络状态,再监听后续变化 return connectivity.onConnectivityChanged .startWith(connectivity.checkConnectivity()); });
第二步:修改myFutureProvider依赖网络状态Provider
final myFutureProvider = FutureProvider<ProductLoadResult<Data>>((ref) async { // 监听网络状态,状态变化时会重新执行该Provider final connectivityResult = await ref.watch(connectivityProvider.future); final netAvailable = connectivityResult != ConnectivityResult.none; if (netAvailable) { final productsApi = ref.read(productsApiProvider); final result = await productsApi.getData(); return ProductLoadResult<Data>(true, result); } else { return ProductLoadResult<Data>(false, null); } });
当网络从不可用变为可用时,myFutureProvider会自动重新发起API请求,无需手动操作。
3. 错误处理优化
原代码中API请求出错会直接进入error分支,可以在myFutureProvider内部捕获API错误,统一用ProductLoadResult返回,让UI处理更一致:
final myFutureProvider = FutureProvider<ProductLoadResult<Data>>((ref) async { final connectivityResult = await ref.watch(connectivityProvider.future); final netAvailable = connectivityResult != ConnectivityResult.none; if (netAvailable) { try { final productsApi = ref.read(productsApiProvider); final result = await productsApi.getData(); return ProductLoadResult<Data>(true, result); } catch (e) { // 返回错误状态,UI可以统一处理 return ProductLoadResult<Data>(false, null, error: e.toString()); } } else { return ProductLoadResult<Data>(false, null); } });
对应的ProductLoadResult新增错误字段:
class ProductLoadResult<T> { final bool hasData; final T? data; final String? error; ProductLoadResult(this.hasData, this.data, {this.error}); }
UI中data分支可以同时处理无网络和API错误的情况:
Widget _bodyForData(ProductLoadResult<Data> result) { if (!result.hasData) { return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Text(result.error ?? "网络不可用"), ElevatedButton( onPressed: () => ref.invalidate(myFutureProvider), child: const Text("重试"), ), ], ), ); } // 渲染正常数据 return ListView(/* ... */); }
总结
- 移除多余的
setState,直接用ref.invalidate触发手动重试 - 通过
StreamProvider监听网络状态,实现自动重新加载 - 统一错误处理逻辑,让UI状态更一致
内容的提问来源于stack exchange,提问作者FetFrumos
相关产品推荐
相关产品推荐

