Flutter RiverPod中合并多个FutureProvider生成新Provider的实现方法
解决方案
你要做的是合并两个FutureProvider返回的AsyncValue状态,把加载、错误、成功三种状态透传到合并后的weatherProvider即可,具体实现如下:
核心逻辑说明
在Riverpod中使用ref.watch监听FutureProvider时,拿到的返回值是封装了加载、错误、数据三种状态的AsyncValue对象,不需要手动await异步请求,只需要把两个子Provider的状态做合并处理即可。
完整实现代码
修改你的weather_provider.dart中的weatherProvider部分:
final weatherProvider = FutureProvider<WeatherData>((ref) async { // 监听两个子Provider的状态 final currentWeatherAsync = ref.watch(currentWeatherProvider); final oneCallWeatherAsync = ref.watch(oneCallWeatherProvider); // 任意子Provider加载中,合并后的Provider也保持加载状态 if (currentWeatherAsync is AsyncLoading || oneCallWeatherAsync is AsyncLoading) { throw const AsyncLoading(); } // 任意子Provider报错,透传错误和堆栈信息到上层 if (currentWeatherAsync.hasError) { throw Error.throwWithStackTrace( currentWeatherAsync.error!, currentWeatherAsync.stackTrace! ); } if (oneCallWeatherAsync.hasError) { throw Error.throwWithStackTrace( oneCallWeatherAsync.error!, oneCallWeatherAsync.stackTrace! ); } // 两个子Provider都加载成功,返回合并后的WeatherData return WeatherData( oneCallWeatherData: oneCallWeatherAsync.requireValue, currentWeatherData: currentWeatherAsync.requireValue, ); });
更简洁的合并写法(适用于Riverpod 2.0+版本)
可以用官方提供的combine扩展方法快速合并两个AsyncValue,不需要手动判断状态:
final weatherProvider = FutureProvider<WeatherData>((ref) { final currentAsync = ref.watch(currentWeatherProvider); final oneCallAsync = ref.watch(oneCallWeatherProvider); // 合并两个AsyncValue的状态,都成功时执行合并逻辑 return currentAsync .combine(oneCallAsync, (currentData, oneCallData) => WeatherData( oneCallWeatherData: oneCallData, currentWeatherData: currentData, )) .when( loading: () => throw const AsyncLoading(), error: (error, stackTrace) => Error.throwWithStackTrace(error, stackTrace), data: (weatherData) => weatherData, ); });
UI层调用示例
合并后的Provider用法和普通FutureProvider完全一致,直接调用.when处理三种状态即可:
Consumer( builder: (context, ref, child) { final weatherAsync = ref.watch(weatherProvider); return weatherAsync.when( loading: () => const CircularProgressIndicator(), error: (error, stackTrace) => Text('天气数据加载失败:$error'), data: (weatherData) { // 直接使用两份数据 final currentTemp = weatherData.currentWeatherData.temperature; final dailyForecast = weatherData.oneCallWeatherData.dailyList; return Column( children: [ Text('当前温度:${currentTemp}°C'), // 渲染每日预报列表 ...dailyForecast.map((item) => Text('${item.date}:${item.tempRange}')), ], ); }, ); }, )
注意事项
- 该实现会并行请求两个接口,性能和直接用
Future.wait的方式完全一致,没有额外损耗 - 如果两个子Provider存在依赖关系(比如第二个接口需要第一个接口返回的地理位置参数),可以直接在子Provider内部通过
ref.watch依赖另一个Provider即可,合并逻辑不需要修改
内容的提问来源于stack exchange,提问作者Ariel
相关产品推荐
相关产品推荐

