关于 ClipPath
我们应该都使用过 ClipXXX 相关的组件, 来实现一些 圆角矩形/圆形形状 十分的方便,那如果想要实现一些奇形怪状的 Widget,例如 五角星/圆弧形 之类的,那就只能用 ClipPath 了。
想要了解 ClipPath ,还是直接去官网撸文档,介绍如下:
A widget that clips its child using a path.
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.
Clipping to a path is expensive.
用 path 来剪切 child 的 widget。
每当要绘制小部件时,都会在委托上调用回调。回调函数返回一个路径,并且该 widget 可防止 child 在 path 外绘制。
裁剪 path 很昂贵。
总的来说,也就是按照路径来剪切子 widget,但是 裁剪 path 很昂贵 。
来看一下怎么使用
关于如何使用,我们还是先来看一下他的构造函数:
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 了。
开始实现自定义形状的 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 - 利用 ClipPath 实现任意形状 Widget的详细内容...