Flutter问题:默认选中的FilterChip无法正常切换状态
FilterChip默认选中后点击无响应的问题排查与解决
问题描述
实现分类列表的FilterChip时,部分Chip已按预期默认选中,但点击进行勾选/取消操作时毫无反应,始终保持初始选中状态。
问题代码
class Filters extends StatefulWidget { final List selected; Filters({Key? key, required this.selected}) : super(key: key); @override _FiltersState createState() => _FiltersState(); } class _FiltersState extends State<Filters> { Future<Map> getData() async { Tmdb instance = Tmdb(); var data = await instance.getGenres(); // Check for error. if (instance.error.isNotEmpty) { data = {'error': instance.error['message'], 'statusCode': instance.error['statusCode']}; } return data; } @override Widget build(BuildContext context) { ... child = Column( children: <Widget>[ Padding( padding: const EdgeInsets.only(left:8.0), child: Align( alignment: Alignment.centerLeft, child: Container( child: Wrap( spacing: 5.0, runSpacing: 3.0, children: <Widget>[ for (var i = 0; i < data['genres'].length; i++) ...[ filterChipWidget( chipName: data['genres'][i]['name'], chipId: data['genres'][i]['id'], isChecked: widget.selected.contains(data['genres'][i]['id']), // Listen for the onSelected callback. onSelected: (isChecked, genreId) { // Store the selected genre id if (isChecked) { if (!selectedItems.contains(genreId)) { selectedItems.add(genreId); } } // Remove the selected genre id else { if (selectedItems.contains(genreId)) { selectedItems.remove(genreId); } } } ), ] ], ) ), ), ), ... } } class filterChipWidget extends StatefulWidget { final String chipName; final int chipId; final bool isChecked; Function(bool, int) onSelected; filterChipWidget({Key? key, required this.chipName, required this.chipId, required this.isChecked, required this.onSelected}) : super(key: key); @override _filterChipWidgetState createState() => _filterChipWidgetState(); } class _filterChipWidgetState extends State<filterChipWidget> { var _isSelected = false; @override Widget build(BuildContext context) { _isSelected = widget.isChecked ? true : false; return FilterChip( label: Text(widget.chipName), labelStyle: TextStyle(color: Color(0xff6200ee),fontSize: 16.0,fontWeight: FontWeight.bold), selected: _isSelected, shape:RoundedRectangleBorder( borderRadius: BorderRadius.circular( 30.0),), backgroundColor: Color(0xffededed), onSelected: (isSelected) { setState(() { _isSelected = isSelected; widget.onSelected(isSelected, widget.chipId); }); }, selectedColor: Color(0xffeadffd), ); } }
问题根源
- 子组件状态被强制重置:
_filterChipWidgetState的build方法中,每次UI重建都会把_isSelected赋值为widget.isChecked,而widget.isChecked依赖父组件初始传入的固定列表widget.selected,导致点击修改的状态被直接覆盖。 - 父组件状态未同步更新:父组件维护的
selectedItems修改后没有调用setState(),无法触发UI重建,子组件的isChecked始终基于初始值,不会随用户操作变化。 - 父组件状态初始化缺失:
selectedItems没有从传入的widget.selected初始化,导致初始选中状态和后续操作的状态源不一致。
解决方案
1. 修正父组件状态管理
- 初始化
selectedItems为传入列表的副本,避免直接引用外部不可变列表。 - 修改
onSelected回调后调用setState(),触发UI重建。 - 子组件的
isChecked改为基于父组件实时的selectedItems判断。
2. 修正子组件状态逻辑
- 不在
build方法中重置_isSelected,而是在initState初始化初始状态,在didUpdateWidget中处理父组件传值变化的情况。
修改后的代码
父组件修改
class Filters extends StatefulWidget { final List selected; Filters({Key? key, required this.selected}) : super(key: key); @override _FiltersState createState() => _FiltersState(); } class _FiltersState extends State<Filters> { late List selectedItems; Future<Map> getData() async { Tmdb instance = Tmdb(); var data = await instance.getGenres(); if (instance.error.isNotEmpty) { data = {'error': instance.error['message'], 'statusCode': instance.error['statusCode']}; } return data; } @override void initState() { super.initState(); // 初始化选中状态为传入列表的副本 selectedItems = List.from(widget.selected); } @override Widget build(BuildContext context) { // ... 其他代码 child = Column( children: <Widget>[ Padding( padding: const EdgeInsets.only(left:8.0), child: Align( alignment: Alignment.centerLeft, child: Container( child: Wrap( spacing: 5.0, runSpacing: 3.0, children: <Widget>[ for (var i = 0; i < data['genres'].length; i++) ...[ filterChipWidget( chipName: data['genres'][i]['name'], chipId: data['genres'][i]['id'], // 基于当前选中状态判断 isChecked: selectedItems.contains(data['genres'][i]['id']), onSelected: (isChecked, genreId) { setState(() { if (isChecked) { if (!selectedItems.contains(genreId)) { selectedItems.add(genreId); } } else { selectedItems.remove(genreId); } }); } ), ] ], ) ), ), ), ], ); // ... 其他代码 } }
子组件修改
class filterChipWidget extends StatefulWidget { final String chipName; final int chipId; final bool isChecked; final Function(bool, int) onSelected; filterChipWidget({Key? key, required this.chipName, required this.chipId, required this.isChecked, required this.onSelected}) : super(key: key); @override _filterChipWidgetState createState() => _filterChipWidgetState(); } class _filterChipWidgetState extends State<filterChipWidget> { late bool _isSelected; @override void initState() { super.initState(); // 初始化选中状态 _isSelected = widget.isChecked; } @override void didUpdateWidget(covariant filterChipWidget oldWidget) { super.didUpdateWidget(oldWidget); // 父组件传值变化时更新子组件状态 if (oldWidget.isChecked != widget.isChecked) { setState(() { _isSelected = widget.isChecked; }); } } @override Widget build(BuildContext context) { return FilterChip( label: Text(widget.chipName), labelStyle: const TextStyle(color: Color(0xff6200ee), fontSize: 16.0, fontWeight: FontWeight.bold), selected: _isSelected, shape: const RoundedRectangleBorder( borderRadius: BorderRadius.circular(30.0), ), backgroundColor: const Color(0xffededed), onSelected: (isSelected) { setState(() { _isSelected = isSelected; widget.onSelected(isSelected, widget.chipId); }); }, selectedColor: const Color(0xffeadffd), ); } }
关键说明
- 父组件通过
setState()更新选中状态后,会触发子组件重新构建,此时子组件能获取到最新的选中状态。 - 子组件仅在初始化和父组件传值变化时更新自身状态,避免了build方法中强制重置状态的问题,保证点击操作的状态能正常保留。
内容的提问来源于stack exchange,提问作者Duddy67
相关产品推荐
相关产品推荐

