Flutter 如何创建价格标签形状的Box容器与边角裁剪卡片视图
Flutter实现价格标签容器与缺口裁剪卡片的方案
这两种不规则容器都可以通过Flutter自带的ClipPath组件结合自定义CustomClipper<Path>实现,无需引入第三方依赖,灵活度最高。
一、缺口边角裁剪卡片实现(对应示例效果)

自定义裁剪类代码
// 自定义卡片边角缺口裁剪器 class CardNotchClipper extends CustomClipper<Path> { // 缺口半径,可自定义 final double notchRadius; // 缺口距离左上角的偏移量,可自定义 final double notchOffset; CardNotchClipper({this.notchRadius = 12, this.notchOffset = 40}); @override Path getClip(Size size) { final path = Path(); // 起点:左上角缺口右侧 path.moveTo(notchOffset + notchRadius, 0); // 顶边到右上角 path.lineTo(size.width, 0); // 右上角圆角 path.arcToPoint(Offset(size.width, 16), radius: const Radius.circular(16)); // 右边到右下角 path.lineTo(size.width, size.height); // 左下角圆角 path.arcToPoint(Offset(0, size.height), radius: const Radius.circular(16)); // 左边到缺口下方 path.lineTo(0, notchOffset + notchRadius); // 画半圆缺口 path.arcToPoint(Offset(0, notchOffset - notchRadius), radius: Radius.circular(notchRadius), clockwise: false); // 左边到左上角缺口左侧 path.lineTo(0, 0); path.lineTo(notchOffset - notchRadius, 0); // 闭合缺口半圆 path.arcToPoint(Offset(notchOffset + notchRadius, 0), radius: Radius.circular(notchRadius), clockwise: false); path.close(); return path; } @override bool shouldReclip(covariant CustomClipper<Path> oldClipper) => false; }
使用方法
ClipPath( clipper: CardNotchClipper(notchRadius: 10, notchOffset: 50), child: Container( width: 300, height: 150, color: Colors.white, padding: const EdgeInsets.all(16), child: const Text("你的卡片内容"), ), )
二、价格标签形状容器实现
自定义裁剪类代码
class PriceTagClipper extends CustomClipper<Path> { final double notchRadius; final double tagRadius; PriceTagClipper({this.notchRadius = 8, this.tagRadius = 8}); @override Path getClip(Size size) { Path path = Path(); path.lineTo(size.width - tagRadius, 0); path.arcToPoint(Offset(size.width, tagRadius), radius: Radius.circular(tagRadius)); path.lineTo(size.width, size.height/2 - notchRadius); path.arcToPoint(Offset(size.width, size.height/2 + notchRadius), radius: Radius.circular(notchRadius), clockwise: false); path.lineTo(size.width, size.height - tagRadius); path.arcToPoint(Offset(size.width - tagRadius, size.height), radius: Radius.circular(tagRadius)); path.lineTo(tagRadius, size.height); path.arcToPoint(Offset(0, size.height - tagRadius), radius: Radius.circular(tagRadius)); path.lineTo(0, tagRadius); path.arcToPoint(Offset(tagRadius, 0), radius: Radius.circular(tagRadius)); path.close(); return path; } @override bool shouldReclip(covariant CustomClipper<Path> oldClipper) => false; }
使用方法和上述卡片一致,将ClipPath的clipper参数替换为PriceTagClipper即可。
注意事项
- 可以自行修改Path的点位、圆角半径、缺口位置来适配你需要的样式
- 如果需要给裁剪后的容器加阴影,不要直接给Container加elevation或者boxShadow,会被ClipPath裁剪掉,建议使用
CustomPaint同时绘制阴影和自定义路径 - 列表中大量使用这类自定义裁剪组件时,建议根据实际场景调整shouldReclip返回值,避免不必要的重绘
内容的提问来源于stack exchange,提问作者LIJU DANIEL
相关产品推荐
相关产品推荐

