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

Flutter实现两侧半圆切角样式优惠券详情Dialog方案

实现两侧带半圆切角的Dialog方案

自定义裁剪方案(推荐)

直接通过CustomClipper绘制带半圆切角的路径,效果精准无适配问题:

1. 定义裁剪器

class CouponClipper extends CustomClipper<Path> {
  final double radius;

  CouponClipper(this.radius);

  @override
  Path getClip(Size size) {
    final path = Path();
    // 左上角起点
    path.moveTo(radius, 0);
    // 上边线
    path.lineTo(size.width - radius, 0);
    // 右上角半圆
    path.arcToPoint(Offset(size.width, radius), radius: Radius.circular(radius));
    // 右边线
    path.lineTo(size.width, size.height - radius);
    // 右下角半圆
    path.arcToPoint(Offset(size.width - radius, size.height), radius: Radius.circular(radius));
    // 下边线
    path.lineTo(radius, size.height);
    // 左下角半圆
    path.arcToPoint(Offset(0, size.height - radius), radius: Radius.circular(radius));
    // 左边线
    path.lineTo(0, radius);
    // 左上角半圆闭合路径
    path.arcToPoint(Offset(radius, 0), radius: Radius.circular(radius));
    return path;
  }

  @override
  bool shouldReclip(CouponClipper oldClipper) => oldClipper.radius != radius;
}

2. 在Dialog中使用

showDialog(
  context: context,
  builder: (context) => Dialog(
    backgroundColor: Colors.transparent, // 透明背景避免默认圆角干扰
    child: ClipPath(
      clipper: CouponClipper(20), // 传入半圆半径
      child: Container(
        color: Colors.white,
        padding: const EdgeInsets.all(16),
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            // 这里放你的优惠券详情内容
            const Text("优惠券详情"),
            // 其他组件...
          ],
        ),
      ),
    ),
  ),
);

替代方案:自定义ShapeBorder

如果不想用ClipPath,可以通过自定义ShapeBorder直接设置Dialog的shape属性:

class CouponShapeBorder extends RoundedRectangleBorder {
  final double radius;

  CouponShapeBorder(this.radius);

  @override
  Path getOuterPath(Rect rect, {TextDirection? textDirection}) {
    final path = Path();
    path.moveTo(radius, rect.top);
    path.lineTo(rect.width - radius, rect.top);
    path.arcToPoint(Offset(rect.width, rect.top + radius), radius: Radius.circular(radius));
    path.lineTo(rect.width, rect.bottom - radius);
    path.arcToPoint(Offset(rect.width - radius, rect.bottom), radius: Radius.circular(radius));
    path.lineTo(radius, rect.bottom);
    path.arcToPoint(Offset(0, rect.bottom - radius), radius: Radius.circular(radius));
    path.lineTo(0, rect.top + radius);
    path.arcToPoint(Offset(radius, rect.top), radius: Radius.circular(radius));
    return path;
  }
}

使用时直接设置Dialog的shape:

showDialog(
  context: context,
  builder: (context) => Dialog(
    shape: CouponShapeBorder(20),
    child: Container(
      padding: const EdgeInsets.all(16),
      child: // 优惠券详情内容
    ),
  ),
);

为什么Stack+Positioned方案效果不好?

这种方式是用背景色块覆盖默认Dialog的边角,不仅容易出现边缘锯齿、适配不同屏幕时错位,还会增加不必要的组件层级,自定义裁剪是直接绘制目标路径,视觉效果更精准。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.30 20:09:24