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

Flutter/Dart通过if条件控制Widget显隐出现随机显隐异常

问题原因

代码存在3处关键逻辑错误,直接导致按钮显示状态随机异常:

  • 状态刷新范围错误:isShow是父组件变量,调用setState仅会触发父页面重建,而showDialog生成的弹窗属于独立的Overlay层组件,不会跟随父页面的setState自动重绘,因此控制台打印的变量值正确,但弹窗内部UI始终持有初始渲染时的旧值,只有当弹窗偶然触发重建时才会同步最新值,表现为随机切换显示状态。
  • Firestore字段名不统一:读取状态时取的是全小写的status字段,点击按钮更新时写入的是首字母大写的Status字段。Firestore对字段名大小写敏感,二者是完全独立的两个字段,长时间运行后会积累大量脏数据,进一步加剧状态不一致问题。
  • 缺少加载态与生命周期校验:弹窗初始化时直接用默认值isShow = true渲染UI,异步请求未返回时用户看到的是错误的初始状态;如果请求返回时弹窗已经关闭,直接调用setState会触发框架异常,干扰正常状态流。
修复代码

弹窗内部使用StatefulBuilder提供局部状态刷新能力,统一字段名,增加加载态与挂载校验,完整可运行代码如下:

updateToolDialog(BuildContext context, id) async {
  return showDialog(
    context: context,
    builder: (dialogContext) {
      return StatefulBuilder(
        builder: (context, setDialogState) {
          bool isShow = true;
          bool isLoading = true;

          // 拉取Firebase状态
          Future<void> fetchStatus() async {
            try {
              final querySnapshot = await FirebaseFirestore.instance
                  .collection('users')
                  .doc(id)
                  .get();
              final statusValue = querySnapshot.get('status').toString();
              // 弹窗已销毁时不再执行UI更新
              if (!dialogContext.mounted) return;

              setDialogState(() {
                isLoading = false;
                isShow = statusValue.contains('yes');
                print("current isShow value: $isShow");
              });
            } catch (e) {
              if (dialogContext.mounted) {
                setDialogState(() => isLoading = false);
              }
            }
          }

          // 弹窗初始化时拉取数据
          fetchStatus();

          return Dialog(
            child: SizedBox(
              width: 600,
              child: Padding(
                padding: const EdgeInsets.all(16.0),
                child: Column(
                  mainAxisSize: MainAxisSize.min,
                  children: [
                    const Text(
                      'Endre verktøy',
                      style: TextStyle(fontWeight: FontWeight.bold),
                    ),
                    const SizedBox(height: 10),
                    if (isLoading)
                      const CircularProgressIndicator()
                    else
                      Visibility(
                        visible: isShow,
                        child: ElevatedButton(
                          onPressed: () async {
                            final firebaseFirestore = FirebaseFirestore.instance;
                            // 统一使用全小写status字段,和读取逻辑保持一致
                            await firebaseFirestore.collection('users').doc(id).update({
                              'status': 'No',
                            });
                            if (dialogContext.mounted) {
                              Navigator.pop(dialogContext, true);
                            }
                            _initializeData();
                          },
                          child: const Text('Change status'),
                        ),
                      ),
                  ],
                ),
              ),
            ),
          );
        },
      );
    },
  );
}
优化提示
  • 不要使用父组件的setState刷新弹窗、底部弹窗这类悬浮组件的UI,这类组件渲染在独立的Overlay容器中,父组件重建不会同步触发悬浮组件重绘,必须使用局部状态管理方案控制其UI更新。
  • 所有Firestore读写操作必须保证字段名大小写完全一致,建议将字段名定义为常量统一调用,避免手写出现拼写、大小写错误。
  • 所有异步操作完成后更新UI前,必须先校验当前组件的mounted属性,避免组件销毁后调用状态方法引发异常和内存泄漏。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 14:33:18