You need to enable JavaScript to run this app. 如何在Flutter中设计实现顶部矩形、底部半圆形的自定义按钮
优惠活动
大模型
产品
解决方案
定价
更多

如何在Flutter中设计实现顶部矩形、底部半圆形的自定义按钮

顶部矩形底部半圆形按钮

Flutter 实现顶部矩形、底部半圆形按钮

实现方案

方案1:自定义ShapeBorder(推荐)

直接适配Flutter原生按钮的shape参数,按钮的水波纹、边框效果都会严格限制在自定义形状内,兼容性最好。

自定义形状类代码

import 'package:flutter/material.dart';

class TopRectBottomCircleBorder extends ShapeBorder {
  final BorderSide side;

  const TopRectBottomCircleBorder({
    this.side = BorderSide.none,
  });

  @override
  EdgeInsetsGeometry get dimensions => EdgeInsets.all(side.width);

  @override
  Path getInnerPath(Rect rect, {TextDirection? textDirection}) {
    return Path()..addPath(getOuterPath(rect, textDirection: textDirection), Offset.zero);
  }

  @override
  Path getOuterPath(Rect rect, {TextDirection? textDirection}) {
    final radius = rect.width / 2;
    Path path = Path();
    path.moveTo(rect.left, rect.top);
    path.lineTo(rect.right, rect.top);
    path.lineTo(rect.right, rect.height - radius);
    path.arcToPoint(
      Offset(rect.left, rect.height - radius),
      radius: Radius.circular(radius),
      clockwise: false,
    );
    path.lineTo(rect.left, rect.top);
    path.close();
    return path;
  }

  @override
  void paint(Canvas canvas, Rect rect, {TextDirection? textDirection}) {
    if (side.style == BorderStyle.none) return;
    canvas.drawPath(getOuterPath(rect, textDirection: textDirection), side.toPaint());
  }

  @override
  ShapeBorder scale(double t) {
    return TopRectBottomCircleBorder(side: side.scale(t));
  }
}

调用示例

ElevatedButton(
  onPressed: () {
    // 点击逻辑
  },
  style: ElevatedButton.styleFrom(
    backgroundColor: Colors.blue,
    foregroundColor: Colors.white,
    // 固定宽高保证底部半圆比例正确
    fixedSize: const Size(120, 140),
    // 应用自定义形状,如需边框可配置side参数
    shape: const TopRectBottomCircleBorder(
      // side: BorderSide(color: Colors.black, width: 2),
    ),
  ),
  child: const Text("按钮"),
)

方案2:ClipPath裁剪(轻量实现)

如果不需要自定义边框,可直接用ClipPath裁剪组件实现,代码更简洁。

实现代码

ClipPath(
  clipper: _BottomCircleClipper(),
  child: ElevatedButton(
    onPressed: () {},
    style: ElevatedButton.styleFrom(
      fixedSize: const Size(120, 140),
      backgroundColor: Colors.blue,
    ),
    child: const Text("按钮"),
  ),
)

class _BottomCircleClipper extends CustomClipper<Path> {
  @override
  Path getClip(Size size) {
    final radius = size.width / 2;
    Path path = Path();
    path.moveTo(0, 0);
    path.lineTo(size.width, 0);
    path.lineTo(size.width, size.height - radius);
    path.arcToPoint(Offset(0, size.height - radius), radius: Radius.circular(radius), clockwise: false);
    path.close();
    return path;
  }

  @override
  bool shouldReclip(covariant CustomClipper<Path> oldClipper) => false;
}

注意事项

  • 按钮宽度需要固定,保证底部半圆的半径等于宽度的1/2,才会形成标准半圆形
  • 两种方案都适配所有Flutter官方按钮组件,可直接替换ElevatedButton为TextButton、OutlinedButton等使用

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.08 06:12:02