Flutter实现选中ListView/GridView组件时背景变暗并高亮组件的方案
Flutter列表项点击/长按高亮+背景变暗实现方案
自定义实现方法
你之前的问题出在把背景模糊/变暗的逻辑放在了列表项内部,导致列表复用和触摸拦截异常。正确思路是用Stack将列表和全局遮罩层分离,单独控制选中项的高亮样式:
核心逻辑
- 用
Stack包裹列表和半透明遮罩层,遮罩默认隐藏,有选中项时显示 - 用
Set<int>维护选中项索引集合,支持多选 - 给列表项绑定点击/长按事件,更新选中状态并控制遮罩显示
- 为选中项添加高亮样式(边框、背景色叠加等)
完整代码示例
import 'package:flutter/material.dart'; class SelectableListDemo extends StatefulWidget { const SelectableListDemo({super.key}); @override State<SelectableListDemo> createState() => _SelectableListDemoState(); } class _SelectableListDemoState extends State<SelectableListDemo> { // 存储选中的项索引 final Set<int> _selectedIndexes = {}; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text("可选中列表")), body: Stack( children: [ // 列表主体 ListView.builder( itemCount: 15, itemBuilder: (ctx, index) { final isSelected = _selectedIndexes.contains(index); return GestureDetector( // 点击切换选中状态 onTap: () { setState(() { isSelected ? _selectedIndexes.remove(index) : _selectedIndexes.add(index); }); }, // 长按直接选中 onLongPress: () { setState(() => _selectedIndexes.add(index)); }, child: Container( margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), padding: const EdgeInsets.all(16), // 选中时的高亮样式 decoration: BoxDecoration( color: isSelected ? Colors.blue.withOpacity(0.1) : Colors.white, border: isSelected ? Border.all(color: Colors.blue, width: 2) : null, borderRadius: BorderRadius.circular(8), ), child: Text("列表项 $index", style: const TextStyle(fontSize: 16)), ), ); }, ), // 背景变暗遮罩层(点击可清空选中) if (_selectedIndexes.isNotEmpty) GestureDetector( onTap: () => setState(() => _selectedIndexes.clear()), child: Container(color: Colors.black.withOpacity(0.4)), ), ], ), ); } }
细节调整
如果需要背景模糊效果,把遮罩层的Container替换为:
BackdropFilter( filter: ImageFilter.blur(sigmaX: 3, sigmaY: 3), child: Container(color: Colors.black.withOpacity(0.2)), )
推荐第三方包
不想自定义的话,可用现成包快速实现:
flutter_multi_select_items:支持多选、高亮、自定义样式,适配ListView/GridViewselectable_container:轻量可选中容器组件,直接嵌套列表项即可
内容的提问来源于stack exchange,提问作者Cavin Macwan
相关产品推荐
相关产品推荐

