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

Flutter Provider ChangeNotifier构建期setState报错解决

setState() or markNeedsBuild() called during build 问题排查与修复

问题1:notifyListeners的更新触发逻辑

ChangeNotifier本身仅维护监听者列表、在收到notifyListeners调用时逐个回调监听方法,自身不会主动触发setState或markNeedsBuild。
触发组件重建的逻辑在Provider侧:当使用Provider.of<T>(context, listen: true)(listen参数默认值为true)获取状态时,Provider会将当前context注册为对应ChangeNotifier的监听者,后续收到notifyListeners的信号时,Provider会对所有注册的context调用markNeedsBuild,标记组件需要在下一帧重建。
你遇到的"called during build"报错,本质是当前组件还处于构建执行阶段,就触发了notifyListeners,Provider尝试标记组件重建,但框架不允许构建过程中再次标记需要重建,直接抛出异常。

问题2:报错根因与修复方案

核心根因

你的报错和UI层写的Provider.of(..., listen: false)没有关系,问题出在unSeen getter的错误实现:你在遍历计算未读数量时,每匹配到一条未读通知就调用一次notifyListeners()。getter是同步读操作,只要组件构建时读取unSeen的值,就会在build流程执行中途触发更新通知,直接触发框架的构建时状态更新校验报错。

修复步骤

  • 移除getter中的notifyListeners调用,保证getter是无副作用的纯计算逻辑
    notifyListeners仅能在通知列表数据发生真实变更的时机调用(比如拉取到新通知、标记通知已读/未读、删除通知等操作完成后),绝对不能在属性读取、build执行过程中调用。
    修正后的unSeen getter代码:
    int get unSeen {
      int notSeen = 0;
      for(Notification notification in _notifications) {
        if(!notification.isSeen) {
          notSeen++;
        }
      }
      return notSeen;
    }
    
    正确的notifyListeners调用示例(对应数据修改方法):
    // 新增通知时
    void addNotification(Notification notification) {
      _notifications.add(notification);
      notifyListeners(); // 数据修改完成后再通知更新
    }
    
    // 标记全部通知已读时
    void markAllSeen() {
      for(var notification in _notifications) {
        notification.isSeen = true;
      }
      notifyListeners(); // 批量修改完成后统一调用一次即可
    }
    
  • 优化UI层状态获取逻辑,避免不必要的rebuild
    你当前写的listen: false虽然不会触发监听注册,但会导致未读数变化时Badge无法自动更新,推荐用Selector做局部精准监听,只在unSeen值变化时刷新Badge组件,性能更好:
    BottomNavigationBarItem(
      icon: Selector<NotificationsModel, int>(
        selector: (context, notiModel) => notiModel.unSeen,
        builder: (context, unSeenCount, iconChild) {
          return Badge(
            showBadge: unSeenCount > 0,
            badgeContent: Text(
              unSeenCount.toString(),
              style: TextStyle(
                color: Colors.white,
                fontFamily: kFontFamily,
                fontWeight: FontWeight.w600,
                fontSize: Dimensions.font7,
              ),
            ),
            child: iconChild,
          );
        },
        child: Icon(dModel.index == 2
            ? Icons.notifications
            : Icons.notifications_paused_outlined),
      ),
      label: 'Notifications',
    ),
    

补充避坑:如果需要在组件初始化阶段触发数据拉取等会调用notifyListeners的逻辑,不要直接写在build方法里,可以放到WidgetsBinding.instance.addPostFrameCallback中执行,等当前帧构建完成后再触发更新,也能避免同类报错。

内容的提问来源于stack exchange,提问作者public static void Main

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 05:06:25