咱們應該都使用過 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。
若有缺陷,但願你們提出,共同窗習!🤝