You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter:ValueListenableNotifier值变更时build期调用showDialog报错解决

解决setState() or markNeedsBuild() called during build错误并正确显示对话框

问题背景

通过AppValueNotifier创建了showStreamDialogNotifier变量,其值随某个流变化,希望值变为true时在Login表单上显示对话框,但调用showDialog时触发了setState() or markNeedsBuild() called during build错误。

错误原因

  1. 构建流程中触发UI更新:ValueListenableBuilder的builder方法属于Widget构建流程的一部分,直接在其中调用showDialog会触发新的UI状态更新,和当前正在执行的build流程冲突,导致报错。
  2. AppValueNotifier实例重复创建:在Login的build方法内实例化AppValueNotifier,每次组件build都会生成新实例,不仅丢失之前的状态,还会导致监听失效。
  3. 错误更新ValueNotifier:updateShowStreamDialogNotifier方法直接重建了ValueNotifier实例,而非修改现有实例的value属性,导致之前的监听者无法收到状态更新通知。

解决方案

1. 修复AppValueNotifier的状态更新逻辑

修改updateShowStreamDialogNotifier方法,直接修改ValueNotifier的value,同时把对话框标题和消息整合到类中统一管理:

class AppValueNotifier {
  final ValueNotifier<bool> showStreamDialogNotifier = ValueNotifier<bool>(false);
  String streamDialogTitle = '';
  String streamDialogMsg = '';

  void updateShowStreamDialogNotifier({
    required bool? newShowStreamDialog,
    required String? newStreamDialogTitle,
    required String? newStreamDialogMsg,
  }) {
    if (newShowStreamDialog != null) {
      // 直接修改value,而非重建ValueNotifier
      showStreamDialogNotifier.value = newShowStreamDialog;
    }
    if (newStreamDialogTitle != null) {
      streamDialogTitle = newStreamDialogTitle;
    }
    if (newStreamDialogMsg != null) {
      streamDialogMsg = newStreamDialogMsg;
    }
  }
}

2. 修复Login组件的监听与对话框触发逻辑

  • 将AppValueNotifier实例提升为State类的成员变量,避免每次build重建
  • 使用WidgetsBinding.instance.addPostFrameCallback延迟执行对话框,确保当前build流程完成后再触发
  • 调整对话框关闭时的状态更新逻辑,确保和AppValueNotifier同步

修改后的Login组件代码:

class Login extends StatefulWidget {
  const Login({super.key});

  @override
  State<Login> createState() => _LoginState();
}

class _LoginState extends State<Login> {
  // 作为State成员,确保组件生命周期内只有一个实例
  final AppValueNotifier appValueNotifier = AppValueNotifier();

  Future<void> showStreamDialog({
    required BuildContext context,
    required String title,
    required String msg,
  }) {
    return showDialog(
      context: context,
      builder: (BuildContext context) {
        return AlertDialog(
          title: Text(title),
          content: SingleChildScrollView(
            child: ListBody(
              children: <Widget>[Text(msg)],
            ),
          ),
          actions: <Widget>[
            TextButton(
              child: const Text('OK'),
              onPressed: () {
                // 关闭对话框后重置状态
                appValueNotifier.updateShowStreamDialogNotifier(
                  newShowStreamDialog: false,
                  newStreamDialogTitle: '',
                  newStreamDialogMsg: '',
                );
                Navigator.pop(context);
              },
            ),
          ],
        );
      },
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          children: [
            Expanded(
              child: ValueListenableBuilder<bool>(
                valueListenable: appValueNotifier.showStreamDialogNotifier,
                builder: (context, value, child) {
                  if (value == true) {
                    // 在当前build完成后执行对话框,避免冲突
                    WidgetsBinding.instance.addPostFrameCallback((_) {
                      showStreamDialog(
                        context: context,
                        title: appValueNotifier.streamDialogTitle,
                        msg: appValueNotifier.streamDialogMsg,
                      );
                    });
                  }
                  return const Text('');
                },
              ),
            ),
          ],
        ),
      ),
    );
  }
}

补充说明

如果原本使用GlobalsProvider管理全局状态,需要确保AppValueNotifier和GlobalsProvider的状态同步,或者直接将对话框相关状态整合到GlobalsProvider中,避免状态分散。

内容的提问来源于stack exchange,提问作者TheMortiestMorty

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 06:04:53