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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.09 19:43:12