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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.02 09:24:35