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

Flutter中ExpansionTile内Checkbox关闭后无法保存选中状态

解决ExpansionTile中Checkbox状态重置问题

问题根源在于ExpansionTile收起时,其子Widget会被销毁,重新展开时会重建SkillWidget的State。当前你的isChecked仅存在于子组件内部,且初始化固定为false,没有从父组件传入的initialChecked正确初始化,导致每次重建都回到未选中状态。

修复步骤:

  1. 正确初始化子组件状态
    在_SkillWidgetState的initState方法中,用父组件传入的initialChecked初始化isChecked,而不是固定赋值false。

  2. 父组件维护状态
    确保父组件在收到onChanged回调时,保存每个SkillWidget的选中状态,下次渲染时将保存的状态通过initialChecked传递回去。

修改后的SkillWidget代码:

import 'package:flutter/material.dart';

class SkillWidget extends StatefulWidget {
  final String skillName;
  final String attribute;
  final ValueNotifier<int> proficiencyBonus;
  final Map<String, int> attributes;
  final ValueChanged<bool?>? onChanged;
  final bool initialChecked;

  const SkillWidget(
    this.skillName,
    this.attribute,
    this.proficiencyBonus,
    this.attributes, {
    Key? key,
    this.onChanged,
    this.initialChecked = false,
  }) : super(key: key);

  @override
  _SkillWidgetState createState() => _SkillWidgetState();
}

class _SkillWidgetState extends State<SkillWidget> {
  late bool isChecked;

  Map<String, String> skillAttributes = {};
  Map<String, int> skillValues = {};

  @override
  void initState() {
    super.initState();
    // 用父组件传入的初始值初始化状态
    isChecked = widget.initialChecked;
  }

  // 当父组件的initialChecked变化时,同步更新子组件状态
  @override
  void didUpdateWidget(covariant SkillWidget oldWidget) {
    super.didUpdateWidget(oldWidget);
    if (oldWidget.initialChecked != widget.initialChecked) {
      setState(() {
        isChecked = widget.initialChecked;
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    final int attributeValue = widget.attributes[widget.attribute] ?? 10;
    final int modifier = ((attributeValue - 10) / 2).floor();
    final num finalValue = modifier + (isChecked ? widget.proficiencyBonus.value : 0);

    return Row(
      children: [
        Expanded(
          child: Row(
            children: [
              Text(
                widget.skillName,
                style: TextStyle(fontWeight: FontWeight.bold),
              ),
              SizedBox(width: 5),
              Text(
                "(${widget.attribute})",
                style: TextStyle(color: Colors.grey),
              ),
            ],
          ),
        ),
        Text(finalValue.toString(), style: TextStyle(fontWeight: FontWeight.bold)),
        Checkbox(
          value: isChecked,
          onChanged: (bool? value) {
            setState(() {
              isChecked = value ?? false;
            });
            widget.onChanged?.call(isChecked);
          },
        ),
      ],
    );
  }
}

父组件示例(关键部分):

假设父组件是一个包含ExpansionTile的页面,需要维护每个技能的选中状态:

class ParentPage extends StatefulWidget {
  @override
  _ParentPageState createState() => _ParentPageState();
}

class _ParentPageState extends State<ParentPage> {
  // 用Map保存每个技能的选中状态,键为技能名称
  final Map<String, bool> _skillCheckedStates = {};

  // 示例技能列表
  final List<String> _skills = ["Acrobatics", "Animal Handling", "Arcana"];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: ExpansionTile(
        title: Text("技能列表"),
        children: _skills.map((skill) {
          return SkillWidget(
            skill,
            "Dexterity", // 示例属性
            ValueNotifier(2), // 示例熟练加值
            {"Dexterity": 14}, // 示例属性值
            initialChecked: _skillCheckedStates[skill] ?? false,
            onChanged: (value) {
              setState(() {
                _skillCheckedStates[skill] = value ?? false;
              });
            },
          );
        }).toList(),
      ),
    );
  }
}

关键说明:

  • initState中初始化isChecked为widget.initialChecked,确保组件第一次创建时使用父组件的状态。
  • didUpdateWidget方法监听父组件传入的initialChecked变化,同步更新子组件状态(如果父组件状态有其他更新场景)。
  • 父组件通过_skillCheckedStates这个Map保存所有技能的选中状态,每次onChanged触发时更新Map,确保ExpansionTile重建子组件时,传入最新的选中状态。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 00:22:49