Flutter中如何实现自定义单选Chips及带图文切换的单选筛选器?
嘿,刚好做过类似的需求,给你分享一个能完美实现你要的效果的方案——未选中的Chips只显示文本,选中时显示图标+文本,同时保证单选逻辑。下面是具体实现:
核心实现思路
- 用
StatefulWidget统一管理选中状态,确保同一组Chips只能有一个被选中 - 自定义Chips组件,根据选中状态动态切换显示内容(文本/图标+文本)和样式
- 通过点击交互触发状态更新,实现单选互斥逻辑
完整代码示例
import 'package:flutter/material.dart'; // 自定义单选Chips组件 class CustomRadioChip extends StatefulWidget { final String label; final String selectedIconAsset; // 选中时显示的图标资源路径 final bool isSelected; final ValueChanged<bool> onSelected; const CustomRadioChip({ super.key, required this.label, required this.selectedIconAsset, required this.isSelected, required this.onSelected, }); @override State<CustomRadioChip> createState() => _CustomRadioChipState(); } class _CustomRadioChipState extends State<CustomRadioChip> { @override Widget build(BuildContext context) { return GestureDetector( // 点击切换选中状态 onTap: () => widget.onSelected(!widget.isSelected), child: Chip( // 根据选中状态切换背景色 backgroundColor: widget.isSelected ? Colors.blue[100] : Colors.grey[100], // 根据选中状态切换边框样式 side: BorderSide( color: widget.isSelected ? Colors.blue : Colors.grey, width: 1.5, ), label: Row( mainAxisSize: MainAxisSize.min, children: [ // 选中时显示图标,未选中则隐藏 if (widget.isSelected) Padding( padding: const EdgeInsets.only(right: 6.0), child: Image.asset( widget.selectedIconAsset, width: 16, height: 16, ), ), Text(widget.label), ], ), ), ); } } // 使用自定义Chips的页面示例 class RadioChipDemo extends StatefulWidget { const RadioChipDemo({super.key}); @override State<RadioChipDemo> createState() => _RadioChipDemoState(); } class _RadioChipDemoState extends State<RadioChipDemo> { String? _selectedOption; // 选项列表,可根据需求修改 final List<String> _options = ['选项1', '选项2', '选项3', '选项4']; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('自定义单选Chips示例')), body: Padding( padding: const EdgeInsets.all(16.0), // 用Wrap实现自动换行布局 child: Wrap( spacing: 8.0, runSpacing: 8.0, children: _options.map((option) { return CustomRadioChip( label: option, // 替换成你自己的图标路径,记得在pubspec.yaml里配置资源 selectedIconAsset: 'assets/check_icon.png', isSelected: _selectedOption == option, onSelected: (isSelected) { setState(() { // 单选逻辑:选中时赋值,取消时置空 _selectedOption = isSelected ? option : null; }); }, ); }).toList(), ), ), ); } }
关键细节提示
- 图标配置:记得把代码里的
assets/check_icon.png换成你自己的图标路径,并且在pubspec.yaml的flutter节点下添加资源配置:assets: - assets/check_icon.png - 样式定制:可以根据你的UI设计,修改
Chip的背景色、边框宽度/颜色、图标大小、文本样式等 - 单选逻辑:父组件通过
_selectedOption变量统一控制所有Chips的选中状态,确保同一时间只有一个选项被选中
替代方案:基于原生Radio改造
如果你更倾向于用Flutter原生Radio的逻辑来实现类似Chips的样式,可以试试这个封装:
import 'package:flutter/material.dart'; class RadioStyleChip extends StatelessWidget { final String label; final String value; final String groupValue; final ValueChanged<String?> onChanged; const RadioStyleChip({ super.key, required this.label, required this.value, required this.groupValue, required this.onChanged, }); @override Widget build(BuildContext context) { return InkWell( onTap: () => onChanged(value), child: Container( padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), decoration: BoxDecoration( color: groupValue == value ? Colors.blue[100] : Colors.grey[100], borderRadius: BorderRadius.circular(20), border: Border.all( color: groupValue == value ? Colors.blue : Colors.grey, width: 1.5, ), ), child: Row( mainAxisSize: MainAxisSize.min, children: [ if (groupValue == value) const Icon(Icons.check, size: 16, color: Colors.blue), if (groupValue == value) const SizedBox(width: 6), Text(label), ], ), ), ); } } // 使用方式和之前类似,父组件管理groupValue即可
内容的提问来源于stack exchange,提问作者Waqar Younus
相关产品推荐
相关产品推荐

