在 Flutter 中自定義 View 有兩種方式:
- 組合已有控制元件
- 自定義繪製
如何自定義繪製
有兩個類做這件事情:
CustomPaint
:會在繪製階段提供一個Canvas
畫布CustomPainter
: 具體的畫筆, 可配置畫筆的顏色,路徑等
CustomPaint(
painter: Sky(),
child: Center(
child: Text(
'Once upon a time...',
style: const TextStyle(
fontSize: 40.0,
fontWeight: FontWeight.w900,
color: Color(0xFFFFFFFF),
),
),
),
)
複製程式碼
當在繪製階段時, CustomPaint
首先會呼叫 painter
在畫布上進行繪製, 然後再繪製它的 child
控制元件, child
繪製完成之後會呼叫 foregroundPainter
進行繪製. 畫布的座標系和 CustomPaint
的座標系匹配. CustomPaint
有個 Size
屬性標識將繪製多大的區域, 繪製時這個 Size
屬性將會傳遞到 CustomPainter
的 paint
方法中, 具體的繪製就在paint
方法中進行, void paint(Canvas canvas, Size size);
的方法簽名中的兩個引數:
- canvas: 用於繪製的畫布, 注意: 該畫布是應用所有控制元件都在使用的, 所以通過這個畫布其實是可以繪製充滿螢幕的內容的
- size: 表示應該繪製的區域大小, 每次繪製都應該限制在這個區域內, 否則可能會覆蓋了其他控制元件的繪製結果
例項一
繪製一個矩形和圓角:
Widget build(BuildContext context) {
return CustomPaint(
painter: ColorPainter(),
size: Size(300, 200),
);
}
複製程式碼
class ColorPainter extends CustomPainter {
final red = Color.fromRGBO(255, 0, 0, 1);
final blue = Color.fromRGBO(0, 0, 255, 1);
@override
void paint(Canvas canvas, Size size) {
final paint = Paint();
final rect = Rect.fromLTRB(0.0, 0.0, size.width, size.height);
// 注意這一句
canvas.clipRect(rect);
paint.color = blue;
canvas.drawRect(rect, paint);
paint.color = red;
canvas.drawCircle(Offset.zero, size.height, paint);
}
@override
bool shouldRepaint(CustomPainter oldDelegate) {
return false;
}
}
複製程式碼
- 定義了繪製區域大小, 為
CustomPaint
中的size
屬性
final rect = Rect.fromLTRB(0.0, 0.0, size.width, size.height);
複製程式碼
- 繪製矩形區域
canvas.drawRect(rect, paint);
複製程式碼
- 繪製圓形
- 圓心偏移量為: 0, 也就是
CustomPaint
的原點 - 半徑為區域的高度
- 圓心偏移量為: 0, 也就是
canvas.drawCircle(Offset.zero, size.height, paint);
複製程式碼
最需要注意的地方我覺得是 canvas.clipRect(rect);
這句. 這句表示只繪製給定的區域中的內容. 如果不寫這句, 效果就是這樣:
為什麼會這樣呢 ?
其實這就是 Size
這個引數的重要性, 因為 Canvas
是被所有控制元件公有的, 如果我們繪製時不指定區域大小, 那在進行一些形狀的繪製時就會出現超出區域的問題.
例項二: 繪製一個帶波紋的 CardView
- 支援顏色配置
- 支援組合外部控制元件 原始碼: wave_card.dart