Flutter GridView滚动后选中状态丢失 如何保留不可见子项数据
Flutter长列表GridView选中状态丢失解决方案
问题根本原因
你当前的实现是把选中状态(颜色变量color)存储在StudentCell的私有State中,而GridView.builder默认会销毁滚动出屏幕的子组件,重新滑入可视区域时会新建组件实例,原有State里的选中状态自然会丢失。你设置的addAutomaticKeepAlives: false也直接关闭了组件状态保活的能力,就算打开这个配置,长列表场景下保活所有组件会占用大量内存,非常不推荐。
正确实现方案
核心思路是状态上移统一管理,子组件不维护选中状态,仅作为展示组件接收参数、响应点击事件:
- 在父组件中用集合存储已选中的学生索引/唯一ID
- 子组件接收「是否选中」的参数和「点击切换选中状态」的回调
- 点击子组件时仅更新父组件的选中集合,由父组件驱动子组件刷新
修改后代码
父组件代码
// 父组件改成StatefulWidget,维护选中状态集合 class StudentCellGridView extends StatefulWidget { const StudentCellGridView({ Key? key, this.crossAxisCount = 4, }) : super(key: key); final int crossAxisCount; @override State<StudentCellGridView> createState() => _StudentCellGridViewState(); } class _StudentCellGridViewState extends State<StudentCellGridView> { // 存储已选中的学生索引,也可以换成学生的唯一ID更稳妥 final Set<int> _selectedIndices = {}; @override Widget build(BuildContext context) { return GridView.builder( addAutomaticKeepAlives: false, // 不需要开启保活,性能更好 addRepaintBoundaries: false, padding: EdgeInsets.only(bottom: defaultPadding), itemCount: 30, gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: widget.crossAxisCount, mainAxisSpacing: defaultPadding, ), itemBuilder: (context, index) => StudentCell( isSelected: _selectedIndices.contains(index), onTap: () { setState(() { if (_selectedIndices.contains(index)) { _selectedIndices.remove(index); } else { _selectedIndices.add(index); } }); }, ), ); } }
子组件代码
// 改成无状态组件,不维护内部状态 class StudentCell extends StatelessWidget { const StudentCell({ Key? key, required this.isSelected, required this.onTap, }) : super(key: key); final bool isSelected; final VoidCallback onTap; @override Widget build(BuildContext context) { return Stack( children: [ TextButton( style: ButtonStyle( overlayColor: MaterialStateColor.resolveWith( (states) => Colors.transparent), ), onPressed: onTap, child: Padding( padding: const EdgeInsets.symmetric(horizontal: defaultPadding), child: Column( children: [ SizedBox( height: 80, child: ClipRRect( borderRadius: BorderRadius.circular(8.0), child: Image.asset("assets/images/Foto.png"), ), ), Expanded( child: Container( child: Center( child: Text( "Child name", maxLines: 3, style: TextStyle(color: Colors.black), overflow: TextOverflow.ellipsis, ), ), ), ), ], ), )), Row( mainAxisAlignment: MainAxisAlignment.end, children: [ Container( width: 20, height: 20, decoration: BoxDecoration( // 根据传入的选中状态决定颜色 color: isSelected ? Colors.green : Colors.grey, shape: BoxShape.circle, ), ), SizedBox( width: defaultPadding, ) ], ), ], ); } }
这种实现方式完全适配长列表场景,没有内存溢出风险,后续需要获取所有选中的学生数据时,直接读取_selectedIndices集合即可。如果你的学生数据有唯一ID,建议把集合的存储类型从索引换成学生ID,避免列表顺序变化时选中状态错位的问题。
内容的提问来源于stack exchange,提问作者Eduardo Airton
相关产品推荐
相关产品推荐

