如何实现Flutter GridView点击时单个容器独立变色显示勾选标记
问题原因
你当前所有网格容器共用同一个_ContainerColor状态变量,点击任意容器触发setState时,所有容器都会读取同一个颜色变量重新渲染,因此会出现联动变色的问题。
实现步骤
- 第一步:封装单条网格项的数据模型,将每个选项的文本、图标、选中状态独立存储,不再用全局单一变量控制所有项的样式
- 第二步:初始化所有选项的列表,每个选项的选中状态独立维护
- 第三步:遍历选项列表生成网格组件,点击时仅修改当前点击项的选中状态,根据单条项的选中状态独立控制容器背景色、右上角勾选标记的显示
核心代码实现
首先在页面State类外定义数据模型:
class FocusOption { final String title; final IconData icon; bool isSelected; FocusOption({ required this.title, required this.icon, this.isSelected = false, }); }
然后在对应页面的State类中,替换原有硬编码的GridView逻辑:
// State类内定义选项列表,把你所有的网格选项都在这里初始化 final List<FocusOption> _focusOptions = [ FocusOption( title: 'Maintaining healthy relationships', icon: MyFlutterApp.relationships, ), // 其余选项按相同格式追加即可 ]; @override Widget build(BuildContext context) { double _height = MediaQuery.of(context).size.height; final data = ModalRoute.of(context)!.settings; String retrieveString = data.arguments == null ? "empty" : data.arguments as String; return Scaffold( resizeToAvoidBottomInset: false, backgroundColor: const Color(0xff31708c), body: Padding( padding: EdgeInsets.only(left: 30, right: 30, top: _height * 0.2), child: Column( children: <Widget>[ Text( 'Hi $retrieveString! What all would you like to focus on?', style: GoogleFonts.montserrat( color: Colors.white70, fontSize: 19, fontWeight: FontWeight.w600 ), textAlign: TextAlign.center, ), const SizedBox(height: 9), Text( "You can pick all that apply:", style: GoogleFonts.montserrat( color: Colors.white70, fontSize: 14.5, fontWeight: FontWeight.w600 ), ), const SizedBox(height: 9), Expanded( child: GridView.count( primary: true, shrinkWrap: true, padding: const EdgeInsets.all(10), childAspectRatio: 1.15, crossAxisCount: 2, crossAxisSpacing: 25, mainAxisSpacing: 25, // 遍历选项列表生成网格项 children: List.generate(_focusOptions.length, (index) { final currentItem = _focusOptions[index]; return GestureDetector( onTap: () { setState(() { // 仅修改当前点击项的选中状态 currentItem.isSelected = !currentItem.isSelected; }); }, child: Stack( children: [ Container( padding: const EdgeInsets.all(8), decoration: BoxDecoration( borderRadius: BorderRadius.circular(15), border: Border.all( color: const Color.fromARGB(255, 20, 83, 106), width: 2.5, ), // 单选项独立控制颜色 color: currentItem.isSelected ? const Color(0xffa1d0e6) : Colors.white ), child: Column( children: [ Align( alignment: Alignment.topCenter, child: Icon( currentItem.icon, color: const Color(0xff31708c), size: 45, ), ), const SizedBox(height: 4), Text( currentItem.title, style: GoogleFonts.montserrat( fontSize: 14, fontWeight: FontWeight.w500, color: const Color(0xff31708c) ), textAlign: TextAlign.center, ) ], ), ), // 选中时显示右上角勾选标记 if(currentItem.isSelected) const Positioned( top: 8, right: 8, child: Icon( Icons.check_circle, color: Color(0xff31708c), size: 20, ), ) ], ), ); }), ), ), ], ), ), ); }
注意事项
- 后续新增网格选项时,只需要往
_focusOptions列表里追加对应的FocusOption实例即可,不需要重复写大量重复的容器代码 - 如果需要获取所有选中的项,遍历
_focusOptions列表筛选isSelected为true的项就能拿到结果
内容的提问来源于stack exchange,提问作者Ta-Ty
相关产品推荐
相关产品推荐

