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

Flutter实现带边框容器内双TextButton(带勾选状态)遇插入难题

解决方案:带勾选状态的双按钮容器实现(Flutter)

核心实现思路

要实现带边框容器内的双按钮点击勾选效果,需要解决三个核心问题:布局适配、选中状态管理、按钮样式与容器样式的协调。以下是完整的实现代码:

import 'package:flutter/material.dart';

class DualSelectButton extends StatefulWidget {
  const DualSelectButton({super.key});

  @override
  State<DualSelectButton> createState() => _DualSelectButtonState();
}

class _DualSelectButtonState extends State<DualSelectButton> {
  // 维护两个按钮的选中状态
  bool _isLeftSelected = false;
  bool _isRightSelected = false;

  // 单选逻辑:点击一个按钮时取消另一个的选中状态
  void _toggleSelection(bool isLeft) {
    setState(() {
      if (isLeft) {
        _isLeftSelected = !_isLeftSelected;
        if (_isLeftSelected) _isRightSelected = false;
      } else {
        _isRightSelected = !_isRightSelected;
        if (_isRightSelected) _isLeftSelected = false;
      }
    });
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      margin: const EdgeInsets.all(20.0),
      decoration: BoxDecoration(
        border: Border.all(color: Colors.blueAccent, width: 3.0),
        borderRadius: BorderRadius.circular(5.0),
      ),
      child: Row(
        children: [
          // 左侧按钮:占容器一半宽度
          Expanded(
            child: TextButton(
              style: ButtonStyle(
                overlayColor: MaterialStateProperty.resolveWith<Color>(
                  (Set<MaterialState> states) {
                    if (states.contains(MaterialState.pressed)) {
                      return Colors.blue.withOpacity(0.2);
                    }
                    return Colors.transparent;
                  },
                ),
                // 按钮背景色:选中时高亮
                backgroundColor: MaterialStateProperty.all(
                  _isLeftSelected ? Colors.blueAccent.withOpacity(0.1) : Colors.transparent,
                ),
                // 按钮圆角:和容器左上角/左下角对齐
                shape: MaterialStateProperty.all<RoundedRectangleBorder>(
                  RoundedRectangleBorder(
                    borderRadius: const BorderRadius.only(
                      topLeft: Radius.circular(2.0),
                      bottomLeft: Radius.circular(2.0),
                    ),
                    side: BorderSide.none,
                  ),
                ),
                padding: MaterialStateProperty.all(
                  const EdgeInsets.symmetric(vertical: 16.0),
                ),
              ),
              onPressed: () => _toggleSelection(true),
              child: Row(
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  // 勾选标记:选中时显示
                  if (_isLeftSelected)
                    const Icon(Icons.check, color: Colors.blueAccent, size: 18.0),
                  const SizedBox(width: 8.0),
                  Text(
                    '选项一',
                    style: TextStyle(
                      color: _isLeftSelected ? Colors.blueAccent : Colors.black87,
                      fontWeight: _isLeftSelected ? FontWeight.bold : FontWeight.normal,
                    ),
                  ),
                ],
              ),
            ),
          ),
          // 分割线:区分两个按钮
          Container(
            width: 1.0,
            height: double.infinity,
            color: Colors.blueAccent.withOpacity(0.3),
          ),
          // 右侧按钮:占容器一半宽度
          Expanded(
            child: TextButton(
              style: ButtonStyle(
                overlayColor: MaterialStateProperty.resolveWith<Color>(
                  (Set<MaterialState> states) {
                    if (states.contains(MaterialState.pressed)) {
                      return Colors.blue.withOpacity(0.2);
                    }
                    return Colors.transparent;
                  },
                ),
                backgroundColor: MaterialStateProperty.all(
                  _isRightSelected ? Colors.blueAccent.withOpacity(0.1) : Colors.transparent,
                ),
                shape: MaterialStateProperty.all<RoundedRectangleBorder>(
                  RoundedRectangleBorder(
                    borderRadius: const BorderRadius.only(
                      topRight: Radius.circular(2.0),
                      bottomRight: Radius.circular(2.0),
                    ),
                    side: BorderSide.none,
                  ),
                ),
                padding: MaterialStateProperty.all(
                  const EdgeInsets.symmetric(vertical: 16.0),
                ),
              ),
              onPressed: () => _toggleSelection(false),
              child: Row(
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  if (_isRightSelected)
                    const Icon(Icons.check, color: Colors.blueAccent, size: 18.0),
                  const SizedBox(width: 8.0),
                  Text(
                    '选项二',
                    style: TextStyle(
                      color: _isRightSelected ? Colors.blueAccent : Colors.black87,
                      fontWeight: _isRightSelected ? FontWeight.bold : FontWeight.normal,
                    ),
                  ),
                ],
              ),
            ),
          ),
        ],
      ),
    );
  }
}

// 使用示例
class DemoPage extends StatelessWidget {
  const DemoPage({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('双按钮勾选示例')),
      body: const Center(child: DualSelectButton()),
    );
  }
}

关键细节说明

  • 布局适配:用Row+Expanded让两个按钮平分容器宽度,中间加入垂直分割线区分按钮;按钮的圆角与容器圆角对齐,避免样式冲突。
  • 状态管理:通过StatefulWidget的布尔变量维护选中状态,点击时切换状态并处理单选逻辑(若需多选,只需去掉取消另一个状态的代码)。
  • UI反馈:选中时显示勾选图标、高亮文本和背景色,点击时的overlay颜色提升交互体验。
  • 样式协调:去掉按钮默认的边框,让容器的外边框统一展示,避免多重边框的视觉混乱。

内容的提问来源于stack exchange,提问作者Batcat456

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.16 07:30:53