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

Flutter报错:setState()/markNeedsBuild()在build期间调用的解决求助

解决Flutter中setState() or markNeedsBuild() called during build错误并实现ToggleButton选中状态跨页面共享

问题描述

我在实现ToggleButton组件时遇到了报错:'setState() or markNeedsBuild() called during build',需求是将激活的ToggleButton索引存储到列表中,以便在其他屏幕展示相同的选中状态。相关代码如下:

class ToggleButtonUsers extends StatefulWidget {
  @override
  _ToggleButtonUsersState createState() => _ToggleButtonUsersState();
}

class _ToggleButtonUsersState extends State<ToggleButtonUsers> {
  bool vertical = false;
  List<bool> selectedUser = [];
  List<Widget> selectedChildren = [];

  @override
  void initState() {
    final userModelData = Provider.of<UserModel>(context, listen: false);
    final userModel = userModelData.items;
    final toggleModelData = Provider.of<ToggleProvider>(context, listen: false);
    final toggleModel = toggleModelData.items;
    final indexUserData = Provider.of<ToggleProvider>(context, listen: false);
    final indexUser = indexUserData.indexUser;
    for (var index = 0; index < toggleModel.length; index += 1) {
      debugPrint('aktueller Index: ' + index.toString());
      selectedUser = List.generate(
        toggleModel.length,
        (index) => index == 0 ? true : false,
      );
      toggleModelData.addIndex(0);
      selectedChildren = List.generate(
        toggleModel.length,
        (index) => UserItem(
          userModel[index].id,
          userModel[index].name,
          userModel[index].image,
          userModel[index].color,
        ),
      );
      //indexUserData.addIndex(0);
    }
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    final indexUserData = Provider.of<ToggleProvider>(context);
    final indexUser = indexUserData.indexUser;
    debugPrint('Inhalt UserIndex ist: ' + indexUser.toString());
    return Padding(
      padding: const EdgeInsets.all(8.0),
      child: ToggleButtons(
        constraints: const BoxConstraints(
          minHeight: 40.0,
          maxHeight: 45,
          minWidth: 80.0,
          maxWidth: 85,
        ),
        borderRadius: BorderRadius.circular(30),
        fillColor: Colors.green,
        //disabledBorderColor: Colors.red,
        direction: vertical ? Axis.vertical : Axis.horizontal,
        onPressed: (int index) {
          if (mounted) {
            setState(() {
              selectedUser[index] = !selectedUser[index];
            });
          }
          selectedUser[index] == true
              ? indexUserData.addIndex(index)
              : indexUserData.removeIndex(10);
          debugPrint('selected User ist nun: ' + selectedUser.toString());
          debugPrint('der gecklickte Index ist: ' + index.toString());
        },
        children: selectedChildren,
        isSelected: selectedUser,
      ),
    );
  }
}

错误原因分析

  1. initState循环逻辑冗余:循环中每次都重新生成selectedUser、selectedChildren,并多次调用toggleModelData.addIndex(0),导致Provider状态被频繁更新,而initState阶段Widget还在构建流程中,可能触发markNeedsBuild()调用时机错误。
  2. onPressed中Provider操作逻辑错误:移除索引时硬编码了10,而非当前操作的index,且未判断索引是否存在于列表中;同时selectedUser的状态更新与Provider操作的顺序可能导致状态不同步。
  3. 初始化逻辑重复:循环次数等于toggleModel.length,但selectedUser和selectedChildren只需要初始化一次即可,重复生成完全没必要。

修正方案及代码

修正步骤

  • 移除initState中的循环,只初始化一次selectedUser和selectedChildren;
  • 初始化Provider的indexUser列表时,只添加一次初始选中的索引(0);
  • 修正onPressed中的移除逻辑,使用当前操作的index,并先判断索引是否存在再移除;
  • 确保状态更新的顺序合理,先更新本地selectedUser,再同步到Provider。

修正后的完整代码

class ToggleButtonUsers extends StatefulWidget {
  @override
  _ToggleButtonUsersState createState() => _ToggleButtonUsersState();
}

class _ToggleButtonUsersState extends State<ToggleButtonUsers> {
  bool vertical = false;
  List<bool> selectedUser = [];
  List<Widget> selectedChildren = [];

  @override
  void initState() {
    super.initState(); // 先调用super.initState(),符合Flutter生命周期规范
    final userModelData = Provider.of<UserModel>(context, listen: false);
    final userModel = userModelData.items;
    final toggleModelData = Provider.of<ToggleProvider>(context, listen: false);
    final toggleModel = toggleModelData.items;
    final indexUserData = Provider.of<ToggleProvider>(context, listen: false);

    // 只初始化一次selectedUser,默认第一个按钮选中
    selectedUser = List.generate(
      toggleModel.length,
      (index) => index == 0,
    );

    // 初始化选中的子组件列表
    selectedChildren = List.generate(
      toggleModel.length,
      (index) => UserItem(
        userModel[index].id,
        userModel[index].name,
        userModel[index].image,
        userModel[index].color,
      ),
    );

    // 清空原有索引列表,添加初始选中的索引0(避免重复添加)
    indexUserData.indexUser.clear();
    indexUserData.addIndex(0);

    debugPrint('初始选中索引: 0');
  }

  @override
  Widget build(BuildContext context) {
    final indexUserData = Provider.of<ToggleProvider>(context);
    final indexUser = indexUserData.indexUser;
    debugPrint('当前存储的选中索引: ' + indexUser.toString());

    return Padding(
      padding: const EdgeInsets.all(8.0),
      child: ToggleButtons(
        constraints: const BoxConstraints(
          minHeight: 40.0,
          maxHeight: 45,
          minWidth: 80.0,
          maxWidth: 85,
        ),
        borderRadius: BorderRadius.circular(30),
        fillColor: Colors.green,
        direction: vertical ? Axis.vertical : Axis.horizontal,
        onPressed: (int index) {
          setState(() {
            selectedUser[index] = !selectedUser[index];
          });

          // 根据新的选中状态更新Provider中的索引列表
          if (selectedUser[index]) {
            if (!indexUser.contains(index)) { // 避免重复添加
              indexUserData.addIndex(index);
            }
          } else {
            if (indexUser.contains(index)) { // 存在才移除
              indexUserData.removeIndex(index);
            }
          }

          debugPrint('当前选中状态: ' + selectedUser.toString());
          debugPrint('点击的索引: ' + index.toString());
        },
        children: selectedChildren,
        isSelected: selectedUser,
      ),
    );
  }
}

额外注意事项

  • 确保你的ToggleProvider中的addIndex和removeIndex方法正确实现,并且在修改列表后调用notifyListeners()来通知UI更新;
  • 如果ToggleProvider的removeIndex方法是根据值移除而非索引,需要确认方法逻辑(比如实现为void removeIndex(int value) { items.remove(value); notifyListeners(); });
  • 生产环境中建议移除不必要的debugPrint语句,避免性能开销。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 15:05:57