Flutter中ExpansionTile内Checkbox关闭后无法保存选中状态
解决ExpansionTile中Checkbox状态重置问题
问题根源在于ExpansionTile收起时,其子Widget会被销毁,重新展开时会重建SkillWidget的State。当前你的isChecked仅存在于子组件内部,且初始化固定为false,没有从父组件传入的initialChecked正确初始化,导致每次重建都回到未选中状态。
修复步骤:
正确初始化子组件状态
在_SkillWidgetState的initState方法中,用父组件传入的initialChecked初始化isChecked,而不是固定赋值false。父组件维护状态
确保父组件在收到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
相关产品推荐
相关产品推荐

