Flutter如何实现3个按钮互斥单选(类似Radio组件效果)
Flutter 三按钮单选实现方案
原有代码问题
- 三个按钮的选中状态、颜色值分散在子组件私有状态和全局静态类
Gcolor中,每个按钮点击仅修改自身状态,没有触发其他按钮的状态重置,因此支持多按钮同时选中。 - 全局静态类存储UI状态的方式会导致状态管理混乱,后续迭代很容易出现状态不同步的问题。
核心实现思路
单选交互的核心原则是状态提升:将所有按钮的选中状态、排序状态统一放到三个按钮的共同父组件中维护,子组件不私自持有状态,只根据父组件传入的参数渲染样式、点击后触发父组件的更新方法。父组件每次点击按钮时更新当前选中项标识,自动重置其他按钮的显示状态,即可实现同一时间仅一个按钮选中的Radio效果。
改造后完整代码
你可以直接删除原有Gcolor类、Dis/Price/New三个独立组件,替换为如下实现,完全保留原有价格/最新按钮的升降序切换逻辑:
class _MyHomePageBodyState extends State<MyHomePageBody> { // 选中按钮标识:0=距离,1=价格,2=最新,null代表无选中 int? selectedIndex; // 排序状态:0=无排序,1=降序,2=升序,仅价格、最新按钮生效 int sortStatus = 0; // 根据选中状态统一返回按钮样式 ButtonStyle _buildButtonStyle(bool isSelected) { return ButtonStyle( side: MaterialStateProperty.all(const BorderSide(color: Colors.blue)), backgroundColor: MaterialStateProperty.all( isSelected ? Colors.blue : Colors.white ), foregroundColor: MaterialStateProperty.all( isSelected ? Colors.white : Colors.blue ), ); } @override Widget build(BuildContext context) { return Container( child: Row( children: [ // 距离按钮 Expanded( child: ElevatedButton( style: _buildButtonStyle(selectedIndex == 0), onPressed: () { setState(() { // 点击已选中的距离按钮则取消选中,否则切换选中距离,重置排序状态 selectedIndex = selectedIndex == 0 ? null : 0; sortStatus = 0; }); }, child: Row( mainAxisAlignment: MainAxisAlignment.center, children: const [ Text("距離 "), Icon(Icons.add_location_outlined, size: 20), ], ), ), ), const SizedBox(width: 5), // 价格按钮 Expanded( child: ElevatedButton( style: _buildButtonStyle(selectedIndex == 1), onPressed: () { setState(() { if (selectedIndex != 1) { // 从其他按钮切换到价格,默认选中+降序 selectedIndex = 1; sortStatus = 1; } else { // 重复点击价格按钮,循环切换:降序->升序->取消选中 sortStatus++; if (sortStatus > 2) { sortStatus = 0; selectedIndex = null; } } }); }, child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ const Text("價格"), const Icon(Icons.attach_money_outlined, size: 15), if (selectedIndex == 1 && sortStatus == 1) const Icon(Icons.arrow_downward, size: 15, color: Colors.yellow), if (selectedIndex == 1 && sortStatus == 2) const Icon(Icons.arrow_upward, size: 15, color: Colors.yellow), ], ), ), ), const SizedBox(width: 5), // 最新按钮 Expanded( child: ElevatedButton( style: _buildButtonStyle(selectedIndex == 2), onPressed: () { setState(() { if (selectedIndex != 2) { selectedIndex = 2; sortStatus = 1; } else { sortStatus++; if (sortStatus > 2) { sortStatus = 0; selectedIndex = null; } } }); }, child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ const Text("最新"), const Icon(Icons.access_time, size: 15), if (selectedIndex == 2 && sortStatus == 1) const Icon(Icons.arrow_downward, size: 15, color: Colors.yellow), if (selectedIndex == 2 && sortStatus == 2) const Icon(Icons.arrow_upward, size: 15, color: Colors.yellow), ], ), ), ), ], ), ); } }
效果说明
- 选中任意按钮后点击其他按钮,原选中按钮自动恢复未选中状态,新按钮切换为选中状态,完全符合Radio单选逻辑
- 保留原有交互:价格、最新按钮首次选中显示降序箭头,第二次点击切换升序箭头,第三次点击取消选中
- 所有状态集中在父组件管理,不会出现状态不同步、多按钮同时选中的问题
内容的提问来源于stack exchange,提问作者Leo
相关产品推荐
相关产品推荐

