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

Flutter使用ValueNotifier在页面build阶段更新文本抛出异常如何解决

问题解决方法

错误根因

你在_buildRow方法中直接修改_myPercentage.value,而_buildRow是ListView.builder构建列表项的回调方法,属于当前页面的build流程内。此时修改ValueNotifier会触发监听它的ValueListenableBuilder调用setState重绘,Flutter禁止在build过程中触发其他组件重绘,因此抛出异常。

解决方案

保留ValueNotifier的前提下,调整计算和更新逻辑即可:

  1. 把逐行扣减占比的逻辑,改成在FutureBuilder拿到完整用户数据后批量计算总占比,再一次性更新剩余百分比
  2. 把首次更新ValueNotifier的操作推到当前帧绘制完成后执行,避开build流程
  3. 移除_buildRow中修改_myPercentage.value的代码,避免build过程中触发状态变更

修改后的核心代码

1. 调整_buildUsersFromDB方法

Widget _buildUsersFromDB() {
  return FutureBuilder<List<User>>(
    future: _userDAO.readData(),
    builder: (BuildContext context, AsyncSnapshot<List<User>> snapshot) {
      if (snapshot.hasData) {
        // 批量计算所有用户的总占比
        final totalUserPercent = snapshot.data!.fold<double>(0, (prev, user) => prev + user.percentage);
        final remainingPercent = 100 - totalUserPercent;
        // 推到当前帧绘制完成后更新,避免build过程中触发重绘
        WidgetsBinding.instance.addPostFrameCallback((_) {
          _myPercentage.value = remainingPercent;
        });
        return ListView.builder(
          itemCount: snapshot.data!.length,
          shrinkWrap: true,
          itemBuilder: (BuildContext context, int i) {
            return Card(
              elevation: 2,
              margin: const EdgeInsets.all(5),
              child: _buildRow(snapshot.data![i]),
            );
          },
        );
      }
      return const Center(child: CircularProgressIndicator());
    },
  );
}

2. 清理_buildRow方法

移除里面修改_myPercentage.value的代码即可,其他逻辑保持不变。

3. 后续用户修改占比的逻辑适配

用户点击列表项修改占比时,属于用户交互回调,不在build流程内,可以直接更新ValueNotifier,示例:

onTap: () => _showDoubleDialog(user).then((newPercent) {
  if (newPercent != null && newPercent != user.percentage) {
    // 计算占比差值
    final diff = newPercent - user.percentage;
    // 直接更新剩余占比,会自动触发环形进度条文本刷新
    _myPercentage.value -= diff;
    // 这里补充更新数据库user.percentage的逻辑即可
  }
}),

build阶段更新状态的最佳实践

  • build相关的回调方法(包括build函数、各类Builder的构建回调)应该是纯函数,仅根据当前状态返回Widget,不要在内部执行修改状态、触发重绘的副作用操作
  • 如果需要在页面渲染完成后更新状态,使用WidgetsBinding.instance.addPostFrameCallback包裹更新逻辑,该回调会在当前帧所有绘制任务结束后执行,不会和build流程冲突
  • 尽量批量计算状态后再一次性更新,不要频繁修改监听类状态,减少不必要的重绘

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.05 00:57:04