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

ChangeNotifier修改值后未更新ListView内CheckBox问题求助

问题:动态ListView内的CheckBox无法随Controller中Map值更新

我有一个包含CheckBox的动态ListView组件,CheckBox的值存储在ListView的State/Controller中。原本以为修改Map里的值后,CheckBox会同步更新,但实际并未生效。


List Controller代码

class ListController with ChangeNotifier {
  ListController();

//<ColumnName, <Row, Value>
  Map<String, Map<int, bool>> _checkBoxValueMap = {};

  Map<String, Map<int, bool>> get checkBoxValueMap => _checkBoxValueMap;

  addCheckBoxValue(String columnName, int row, bool isEnabled) {
    Map<String, Map<int, bool>> tempCheckBoxValueMap = _checkBoxValueMap;
    if (tempCheckBoxValueMap[columnName] == null) {
      tempCheckBoxValueMap[columnName] = {};
    }
    tempCheckBoxValueMap[columnName]![row] = isEnabled;
    _checkBoxValueMap = tempCheckBoxValueMap;
  }

  setCheckBoxValue(String columnName, int row, bool isChecked) {
    Map<String, Map<int, bool>> tempCheckBoxValueMap = _checkBoxValueMap;
    tempCheckBoxValueMap[columnName]![row] = isChecked;
    _checkBoxValueMap = tempCheckBoxValueMap;
    notifyListeners();
  }
}

我以列名和行号为键,将CheckBox的值存在嵌套Map中,通过setCheckBoxValue方法设置单个CheckBox的值(该操作在列表构建完成并显示后从外部调用),调用代码如下:

ListController listController =
                    controlStateList["DetailsList"]!["State"] as ListController;
                listController.setCheckBoxValue("Approve", 1, true);

CheckBox组件代码

class ListCheckBox extends StatefulWidget {
  ListCheckBox({
    Key? key,
    required this.parentControlName,
    required this.parentController,
    required this.rowIndex,
    required this.columnName,
  }) : super(key: key);

  final String parentControlName;
  final ListController parentController;
  final int rowIndex;
  final String columnName;

  @override
  State<StatefulWidget> createState() => ListCheckBoxState();
}

class ListCheckBoxState extends State<ListCheckBox> {
  @override
  Widget build(BuildContext context) {
    PageLoader pageLoader = context.watch<PageLoader>();
    return Checkbox(
      value: widget.parentController
          .checkBoxValueMap[widget.columnName]![widget.rowIndex],
      fillColor: MaterialStateProperty.all(
          widget.parentController.enable ? Colors.blue : Colors.black),
      onChanged: (newValue) async {
        if (widget.parentController.enable) {
          await checkEditBoxChanges(pageLoader);
          //send request
          Map<String, String> requestData = {
            "Session": sessionInfo.sessionID,
            "ProgramID": pageLoader.pgmID!,
            "ControlName": widget.parentControlName,
            "SelectedRow": widget.rowIndex.toString(),
            "Data": newValue! ? "Y" : "",
            "FunctionKey": FunctionKeys.listCheckBox,
            "ColumnName": widget.columnName
          };
          await sessionInfo
              .sendRequest("EditData", requestData)
              .then((response) {
            widget.parentController
                .setCheckBoxValue(widget.columnName, widget.rowIndex, newValue);
            Map<String, dynamic> responseData = jsonDecode(response);
            FormFunctions.processResponse(pageLoader, responseData,
                build: false);
          });
        }
      },
    );
  }
}

列表中添加CheckBox的代码

widget.controller.addCheckBoxValue(
                  columnName, rowIndex + 1, rowDataListItem[columnName] == "Y");
              rowData.add(ConstrainedBox(
                constraints: BoxConstraints(minHeight: 20),
                child: Container(
                    width: Utility.getTextWidth("${longestItem[columnName]}ww",
                            StandardSize.font1) +
                        10,
                    child: ListCheckBox(
                      parentController: widget.controller,
                      parentControlName: widget.controlName,
                      rowIndex: rowIndex + 1,
                      columnName: columnName,
                    )),
              ));

解决方案

问题根源有两点:

  1. Controller中Map的更新是浅拷贝,未改变引用,Flutter无法检测到变化
  2. CheckBox组件未监听Controller的通知,不会触发重新构建

修复步骤

1. 修复ListController的Map更新逻辑

修改addCheckBoxValue和setCheckBoxValue方法,创建Map的新实例,确保引用发生变化:

class ListController with ChangeNotifier {
  ListController();

//<ColumnName, <Row, Value>
  Map<String, Map<int, bool>> _checkBoxValueMap = {};

  Map<String, Map<int, bool>> get checkBoxValueMap => _checkBoxValueMap;

  addCheckBoxValue(String columnName, int row, bool isEnabled) {
    // 创建外层Map的副本
    final tempCheckBoxValueMap = Map<String, Map<int, bool>>.from(_checkBoxValueMap);
    // 内层Map不存在则新建,否则创建副本
    tempCheckBoxValueMap[columnName] = Map<int, bool>.from(tempCheckBoxValueMap[columnName] ?? {});
    tempCheckBoxValueMap[columnName]![row] = isEnabled;
    _checkBoxValueMap = tempCheckBoxValueMap;
    notifyListeners();
  }

  setCheckBoxValue(String columnName, int row, bool isChecked) {
    // 创建外层Map的副本
    final tempCheckBoxValueMap = Map<String, Map<int, bool>>.from(_checkBoxValueMap);
    // 确保内层Map存在且为新实例
    if (tempCheckBoxValueMap[columnName] == null) {
      tempCheckBoxValueMap[columnName] = {};
    } else {
      tempCheckBoxValueMap[columnName] = Map<int, bool>.from(tempCheckBoxValueMap[columnName]!);
    }
    tempCheckBoxValueMap[columnName]![row] = isChecked;
    _checkBoxValueMap = tempCheckBoxValueMap;
    notifyListeners();
  }
}

2. 让CheckBox组件监听Controller变化

如果Controller是通过Provider全局提供的,直接在build方法中监听:

class ListCheckBoxState extends State<ListCheckBox> {
  @override
  Widget build(BuildContext context) {
    PageLoader pageLoader = context.watch<PageLoader>();
    // 监听Controller变化,触发重新构建
    final controller = context.watch<ListController>();
    return Checkbox(
      value: controller.checkBoxValueMap[widget.columnName]![widget.rowIndex],
      fillColor: MaterialStateProperty.all(
          controller.enable ? Colors.blue : Colors.black),
      onChanged: (newValue) async {
        if (controller.enable) {
          await checkEditBoxChanges(pageLoader);
          // 请求逻辑不变
          Map<String, String> requestData = {
            "Session": sessionInfo.sessionID,
            "ProgramID": pageLoader.pgmID!,
            "ControlName": widget.parentControlName,
            "SelectedRow": widget.rowIndex.toString(),
            "Data": newValue! ? "Y" : "",
            "FunctionKey": FunctionKeys.listCheckBox,
            "ColumnName": widget.columnName
          };
          await sessionInfo
              .sendRequest("EditData", requestData)
              .then((response) {
            controller.setCheckBoxValue(widget.columnName, widget.rowIndex, newValue);
            Map<String, dynamic> responseData = jsonDecode(response);
            FormFunctions.processResponse(pageLoader, responseData,
                build: false);
          });
        }
      },
    );
  }
}

如果Controller是通过widget传递的,手动添加监听器:

class ListCheckBoxState extends State<ListCheckBox> {
  @override
  void initState() {
    super.initState();
    // 添加监听器,Controller变化时触发UI更新
    widget.parentController.addListener(() {
      setState(() {});
    });
  }

  @override
  void dispose() {
    // 移除监听器,避免内存泄漏
    widget.parentController.removeListener(() {
      setState(() {});
    });
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    PageLoader pageLoader = context.watch<PageLoader>();
    return Checkbox(
      value: widget.parentController.checkBoxValueMap[widget.columnName]![widget.rowIndex],
      fillColor: MaterialStateProperty.all(
          widget.parentController.enable ? Colors.blue : Colors.black),
      onChanged: (newValue) async {
        if (widget.parentController.enable) {
          await checkEditBoxChanges(pageLoader);
          // 请求逻辑不变
          Map<String, String> requestData = {
            "Session": sessionInfo.sessionID,
            "ProgramID": pageLoader.pgmID!,
            "ControlName": widget.parentControlName,
            "SelectedRow": widget.rowIndex.toString(),
            "Data": newValue! ? "Y" : "",
            "FunctionKey": FunctionKeys.listCheckBox,
            "ColumnName": widget.columnName
          };
          await sessionInfo
              .sendRequest("EditData", requestData)
              .then((response) {
            widget.parentController.setCheckBoxValue(widget.columnName, widget.rowIndex, newValue);
            Map<String, dynamic> responseData = jsonDecode(response);
            FormFunctions.processResponse(pageLoader, responseData,
                build: false);
          });
        }
      },
    );
  }
}

核心说明

  • Flutter依赖引用变化检测状态更新,可变集合(Map/List)必须创建新实例才能触发ChangeNotifier的通知
  • 组件必须监听ChangeNotifier的通知,才能在Controller数据变化时重新构建UI

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 00:54:56