Riverpod FutureProvider的AsyncValue.when在VoidCallback中无响应问题
问题
点击按钮时触发FutureProvider,期望根据返回值修改应用状态,但点击后仅loading回调触发,数据返回时data回调未执行,推测是组件重建导致onTap执行中断。尝试过AsyncValue.whenData,回调仍未触发。
现有代码
按钮onTap回调
ElevatedButton( onPressed: () { final promptResp = ref.watch( getProvider( Tuple2( '/gen-image', {'prompt': controller.text}, ), ), ); promptResp.when<void>(data: (value) { ref.read(isLoading.notifier).state = false; final data = jsonDecode(value.body); ref.read(imagesProvider.notifier).state = data; }, error: (e, r) { return; }, loading: () { ref.read(isLoading.notifier).state = true; }); }, )
对应的Provider代码
final getProvider = FutureProvider.family( (ref, Tuple2<String?, Map<String, dynamic>?> info) async { final path = info.item1; final params = info.item2; final url = backendUrl().replace(queryParameters: params, path: path); final resp = await http.get(url, headers: { 'X-FIREBASE_TOKEN': await ref.read(authProvider).currentUser!.getIdToken() }); return resp; });
解决方案
核心问题是在onPressed回调内直接watch FutureProvider:watch依赖组件的BuildContext,当isLoading状态变化触发组件重建时,原来的onPressed上下文已失效,后续的data回调自然无法执行。
推荐两种可行的修正方式:
方式一:手动处理异步流程(更直观)
直接通过ref.read()获取FutureProvider的future,手动控制加载、成功、错误状态:
ElevatedButton( onPressed: () async { final ref = ref; // 保留ref引用,避免组件重建后丢失 ref.read(isLoading.notifier).state = true; try { // 读取Provider的future并等待结果 final resp = await ref.read(getProvider( Tuple2( '/gen-image', {'prompt': controller.text}, ), ).future); // 处理返回数据 final data = jsonDecode(resp.body); ref.read(imagesProvider.notifier).state = data; } catch (e, stackTrace) { // 这里可以添加错误处理逻辑,比如弹窗提示 } finally { // 无论成功或失败,都结束加载状态 ref.read(isLoading.notifier).state = false; } }, )
方式二:使用ref.listen监听状态变化
通过ref.listen订阅Provider的状态变化,即使组件重建,监听依然有效(完成后需手动关闭监听避免内存泄漏):
ElevatedButton( onPressed: () { // 创建Provider实例 final imageGenProvider = getProvider( Tuple2( '/gen-image', {'prompt': controller.text}, ), ); // 订阅状态变化 final listener = ref.listen(imageGenProvider, (previous, next) { next.when( data: (value) { ref.read(isLoading.notifier).state = false; final data = jsonDecode(value.body); ref.read(imagesProvider.notifier).state = data; listener.close(); // 完成后关闭监听 }, error: (e, r) { ref.read(isLoading.notifier).state = false; listener.close(); // 错误时也关闭监听 // 添加错误处理逻辑 }, loading: () { ref.read(isLoading.notifier).state = true; }, ); }); }, )
总结
- 方式一代码更简洁,逻辑清晰,适合大多数场景;
- 方式二更适合需要持续监听Provider状态的复杂场景。
内容的提问来源于stack exchange,提问作者Mudassir
相关产品推荐
相关产品推荐

