咱们应该都使用过 ClipXXX
相关的组件, 来实现一些 圆角矩形/圆形形状十分的方便,那若是想要实现一些奇形怪状的 Widget,例如 五角星/圆弧形之类的,那就只能用 ClipPath
了。git
想要了解 ClipPath
,仍是直接去官网撸文档,介绍以下:github
A widget that clips its child using a path.markdown
Calls a callback on a delegate whenever the widget is to be painted. The callback returns a path and the widget prevents the child from painting outside the path.ide
Clipping to a path is expensive.函数
用 path 来剪切 child 的 widget。post
每当要绘制小部件时,都会在委托上调用回调。回调函数返回一个路径,而且该 widget 可防止 child 在 path 外绘制。性能
裁剪 path 很昂贵。学习
总的来讲,也就是按照路径来剪切子 widget,可是裁剪 path 很昂贵。ui
关于如何使用,咱们仍是先来看一下他的构造函数:this
const ClipPath({
Key key,
this.clipper, // final CustomClipper<Path> clipper;
this.clipBehavior = Clip.antiAlias,
Widget child,
}) : assert(clipBehavior != null),
super(key: key, child: child);
复制代码
首先能够看到须要的参数其实就两个,一个是 clipper
,另外一个是 child
。
child
就是被 clipper
裁剪的组件,具体是啥本身来写就好了,剩下的就是 clipper
。
看一下 clipper
的源码:
/// CustomClipper
abstract class CustomClipper<T> {
/// Creates a custom clipper.
///
/// The clipper will update its clip whenever [reclip] notifies its listeners.
const CustomClipper({ Listenable reclip }) : _reclip = reclip;
final Listenable _reclip;
/// 返回 clip 的说明 -> T
T getClip(Size size);
/// 是否从新 clip
bool shouldReclip(covariant CustomClipper<T> oldClipper);
}
复制代码
这里去掉了一些方法,只保留了咱们须要重写的,其中最主要的就是 T getClip(Size size)
。
在 ClipPath
里传入的泛型为 <Path>
,其实咱们熟知的 ClipRect
/ ClipRRect
/ ClipOval
也就是对应着 CustomClipper<Rect>
/ CustomClipper<RRect>
/ CustomClipper<Rect>
而已。
因此在这里咱们只须要定义好本身的 Path
就能够实现任意形状的 Widget 了。
咱们来实现以下形状(上面是原图、下面是裁剪过的):
综上所述,只须要实现一个 CustomClipper<Path>
而后传入ClipPath
的 clipper
参数便可。
代码以下:
class MyClipper extends CustomClipper<Path> {
@override
Path getClip(Size size) {
Path path = Path();
// 从 60,0 开始
path.moveTo(60, 0);
// 二阶贝塞尔曲线画弧
path.quadraticBezierTo(0, 0, 0, 60);
// 链接到底部
path.lineTo(0, size.height / 1.2);
// 三阶贝塞尔曲线画弧
path.cubicTo(size.width / 4, size.height, size.width / 4 * 3, size.height / 1.5, size.width, size.height / 1.2);
// 再链接回去
path.lineTo(size.width, 60);
// 再用二阶贝塞尔曲线画弧
path.quadraticBezierTo(size.width - 60, 60, size.width - 60, 0);
return path;
}
@override
bool shouldReclip(CustomClipper<Path> oldClipper) => false;
}
复制代码
逻辑就不说啦,都在注释里。
由于ClipPath
的消耗比较大,因此若是只是想裁剪个圆角之类的,仍是推荐使用自带的 ClipRRect
之类的,他们的性能更好(官方文档所说)。
ClipPath
还有一个静态方法 ClipPath.shape()
,这个具体就不说了,有感兴趣的能够去翻源码查看。
也能够看看 张风捷特烈的这篇文章 - 【Flutter高级玩法-shape】Path在手,天下我有。
这篇文章详细的讲解了 Path
的玩法,只有你想不到,没有它作不到!在最后也有讲解该静态方法。
代码已经提交到了 Github - 裁剪 Widget Demo。
若有缺陷,但愿你们提出,共同窗习!🤝