如何从AsyncValue的error回调中弹出AlertDialog?
处理Riverpod AsyncValue错误状态下弹出AlertDialog的问题
问题描述
使用Riverpod从API加载数据时,希望在API请求失败(AsyncValue处于error状态)时弹出AlertDialog。最初在AsyncValue.when的error回调中直接调用showDialog,运行时触发组件脏状态异常;尝试使用SchedulerBinding.instance.addPostFrameCallback()和context.mounted仍未解决问题。
原始核心代码片段:
final vehiclesList = ref.watch(vehiclesProvider); // ... vehiclesList.when( // data和loading回调省略 error: ((error, stackTrace) { showDialog(/* ... */); return Container(); }), )
解决方案
核心问题是不要在build方法内的UI构建回调(如when)中触发弹窗这类副作用——build流程仅负责生成UI,在此期间修改导航栈(弹窗属于导航操作)会导致组件树状态不一致,引发脏状态异常。正确做法是使用ref.listen监听Provider状态变化,在状态变为error时执行弹窗逻辑。
步骤1:用ref.listen监听状态变化
在build方法中添加状态监听,仅当状态从非error变为error时触发弹窗,避免重复弹出:
@override Widget build(BuildContext context, WidgetRef ref) { // 监听vehiclesProvider的状态变化 ref.listen<AsyncValue<List<Vehicle>>>(vehiclesProvider, (previous, next) { // 仅当之前无错误,当前出现错误时才弹窗 if (previous?.hasError != true && next.hasError) { if (context.mounted) { // 确保上下文仍有效 _showAlertDialog(context); } } }); final vehiclesList = ref.watch(vehiclesProvider); // 后续UI构建逻辑不变 return Scaffold(/* ... */); }
步骤2:修改when的error回调
移除when回调中的弹窗逻辑,仅返回错误状态下的UI(如空容器或错误提示):
vehiclesList.when( data: (vehicles) { /* 数据展示逻辑 */ }, error: ((error, stackTrace) { // 仅返回UI,不执行副作用 return const Center(child: Text('加载失败,请稍后重试')); }), loading: () => const Center(child: CircularProgressIndicator()), )
步骤3:保留弹窗方法
弹窗方法不变,但要注意使用dialogContext管理弹窗的关闭:
void _showAlertDialog(BuildContext context) { showDialog( context: context, builder: (dialogContext) { return AlertDialog( title: const Text('提示'), content: const Text('车辆列表加载失败,请返回重试'), actions: <Widget>[ TextButton( child: const Text('确定'), onPressed: () { Navigator.of(dialogContext).pop(); // 关闭弹窗 Navigator.of(context).pop(); // 返回上一页 }, ), ], ); }, ); }
关键说明
ref.listen是Riverpod专门用于监听状态变化并执行副作用(如弹窗、网络请求)的API,不会干扰build流程,避免脏状态异常。- 判断
context.mounted可防止上下文已销毁时执行导航操作,避免内存泄漏或异常。 - 通过对比
previous和next的状态,可避免错误状态重复触发时弹窗多次弹出。
内容的提问来源于stack exchange,提问作者realmikep
相关产品推荐
相关产品推荐

