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

Flutter GridView滚动后选中状态丢失 如何保留不可见子项数据

Flutter长列表GridView选中状态丢失解决方案

问题根本原因

你当前的实现是把选中状态(颜色变量color)存储在StudentCell的私有State中,而GridView.builder默认会销毁滚动出屏幕的子组件,重新滑入可视区域时会新建组件实例,原有State里的选中状态自然会丢失。你设置的addAutomaticKeepAlives: false也直接关闭了组件状态保活的能力,就算打开这个配置,长列表场景下保活所有组件会占用大量内存,非常不推荐。

正确实现方案

核心思路是状态上移统一管理,子组件不维护选中状态,仅作为展示组件接收参数、响应点击事件:

  1. 在父组件中用集合存储已选中的学生索引/唯一ID
  2. 子组件接收「是否选中」的参数和「点击切换选中状态」的回调
  3. 点击子组件时仅更新父组件的选中集合,由父组件驱动子组件刷新

修改后代码

父组件代码

// 父组件改成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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.01 01:48:04