Flutter技术咨询:如何创建此类自定义单选按钮组
嘿,我来教你怎么在Flutter里做出符合你需求的自定义单选按钮组!其实核心就是灵活利用状态管理和自定义布局,下面给你两种实用方案,你可以根据自己的样式需求来选:
方案一:基于RadioListTile快速改造(适合轻度定制)
如果只是想调整默认单选组的颜色、图标这类细节,用Flutter自带的RadioListTile改造最省事,不用从零搭布局:
import 'package:flutter/material.dart'; class CustomRadioGroup extends StatefulWidget { const CustomRadioGroup({super.key}); @override State<CustomRadioGroup> createState() => _CustomRadioGroupState(); } class _CustomRadioGroupState extends State<CustomRadioGroup> { String? selectedOption; final List<String> options = ['选项A', '选项B', '选项C', '选项D']; @override Widget build(BuildContext context) { return Column( children: options.map((option) { return RadioListTile( title: Text(option), value: option, groupValue: selectedOption, onChanged: (value) => setState(() => selectedOption = value), // 自定义选中时的主色调 activeColor: Colors.blueAccent, // 选中时的背景高亮色 selectedTileColor: Colors.blueAccent.withOpacity(0.1), // 把单选按钮放在文字右边(默认在左边) controlAffinity: ListTileControlAffinity.trailing, // 替换默认的单选图标 secondary: selectedOption == option ? const Icon(Icons.check_circle, color: Colors.blueAccent) : const Icon(Icons.radio_button_unchecked, color: Colors.grey), ); }).toList(), ); } }
你可以随便调整activeColor、selectedTileColor,甚至替换图标,快速得到符合设计的样式。
方案二:完全自定义单选组(适合高度定制)
如果你的设计要求很独特(比如自定义按钮形状、渐变背景、动画效果),那就得自己从头构建布局,用InkWell或者GestureDetector来处理点击,手动管理选中状态:
import 'package:flutter/material.dart'; class FullyCustomRadioGroup extends StatefulWidget { const FullyCustomRadioGroup({super.key}); @override State<FullyCustomRadioGroup> createState() => _FullyCustomRadioGroupState(); } class _FullyCustomRadioGroupState extends State<FullyCustomRadioGroup> { String? selectedValue; // 可以存储更多选项信息,比如图标、描述等 final List<Map<String, dynamic>> options = [ {'id': 'opt1', 'label': '自定义选项1'}, {'id': 'opt2', 'label': '自定义选项2'}, {'id': 'opt3', 'label': '自定义选项3'}, ]; @override Widget build(BuildContext context) { return Column( children: options.map((opt) { final isSelected = selectedValue == opt['id']; return InkWell( onTap: () => setState(() => selectedValue = opt['id']), borderRadius: BorderRadius.circular(8), // 用AnimatedContainer实现平滑过渡动画 child: AnimatedContainer( duration: const Duration(milliseconds: 200), padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), margin: const EdgeInsets.symmetric(vertical: 4, horizontal: 16), decoration: BoxDecoration( color: isSelected ? Colors.purple[100] : Colors.white, border: Border.all( color: isSelected ? Colors.purple : Colors.grey.shade300, width: 2, ), borderRadius: BorderRadius.circular(8), boxShadow: isSelected ? [ BoxShadow( color: Colors.purple.withOpacity(0.2), blurRadius: 4, offset: const Offset(0, 2), ) ] : null, ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( opt['label'], style: TextStyle( fontSize: 16, color: isSelected ? Colors.purple : Colors.black87, fontWeight: isSelected ? FontWeight.w600 : FontWeight.normal, ), ), // 自定义选中标记 Container( width: 24, height: 24, decoration: BoxDecoration( shape: BoxShape.circle, color: isSelected ? Colors.purple : Colors.transparent, border: Border.all( color: isSelected ? Colors.purple : Colors.grey.shade400, width: 2, ), ), child: isSelected ? const Icon( Icons.check, size: 16, color: Colors.white, ) : null, ), ], ), ), ); }).toList(), ); } }
这个方案里你能完全控制每个元素的样式,甚至可以给选中状态加阴影、渐变,或者调整动画时长,完全贴合你的设计图。
如果需要在多个页面共享选中状态,还可以用Provider、Riverpod这类状态管理工具来替代setState,不过上面的代码已经能满足大部分场景啦!
内容的提问来源于stack exchange,提问作者Shyju M
相关产品推荐
相关产品推荐

