Flutter是個「萬事萬物皆Widget」的程式語言。
但這代表了什麼特殊意義嗎?
如果用Android的原生語言框架來說明和對比,可能會比較清楚。
Android的原生框架中,介面元件是View,而所有的View都有個可以從View內獲取Canvas的函數「getView」。相較之下,Flutter的Widget就不具備此特性。
(這延伸出一個問題:這兩種框架Render的方式一樣嗎?顯然有不同,但先不去探究。)
在Android中,要進行Canvas繪圖,可以直接使用View或任何擴充自View的物件。差別只在於效能而已。
但在Flutter中,「建議」經由CustomPainter來進行Canvas繪圖。
abstract class CustomPainter extends Listenable {
void paint(Canvas canvas, Size size);
}
如上面的程式碼顯示,只要擴充「CustomPainter」,就能使用「paint」函數來取得Canvas進行自己想要的繪圖工作。
但是CustomPainter不是個Widget,所以無法在Widget組成的WidgetTree中使用,必須要使用「CustomPaint」當作Widget,然後將「CustomPainter」當作前者的傳入參數來使用。
class CustomPaint extends SingleChildRenderObjectWidget {
const CustomPaint({
super.key,
this.painter,
this.foregroundPainter,
this.size = Size.zero,
this.isComplex = false,
this.willChange = false,
super.child,
}) : assert(painter != null || foregroundPainter != null || (!isComplex && !willChange));
}
更具體一點的使用範例,如下:
@override
Widget build(BuildContext context) {
return CustomPaint(painter: CustomPainter()...);
}
來做個簡單的圖形吧!
像這樣畫「一朵花」。(這是花~這是花~這是花~這是一朵花~~你要說它不是花、像別的東西我也無所謂~~)
圖中雖然是兩朵,但其實是一朵花畫兩次。
@override
void paint(Canvas canvas, Size size) {
Paint paint = Paint();
paint.color = const Color(0xff000000);
paint.strokeWidth = size.width/15;
paint.strokeCap = StrokeCap.round;
final center = Offset(size.width / 2, size.height / 2);
final radius = size.width / 2;
for (int i = 0; i < 720; i++) {
if(i % 30 == index){
// 計算每條線的角度
double angle = ((reverse)?-1:1) * 2 * pi / 720 * i;
// 使用 sin 和 cos 函數來計算終點的 x 和 y 座標
double x = center.dx + radius * cos(angle);
double y = center.dy + radius * sin(angle);
double x2 = center.dx + (radius * cos(angle)/1.8);
double y2 = center.dy + (radius * sin(angle)/1.8);
// 畫一條線,從圓心到計算出的終點
canvas.drawLine(Offset(x2, y2), Offset(x, y), paint);
}
}
}
這裡的paint函數中有傳入一組「Size size」,這是CustomPaint這個Widget的大小。同時會以這個Widget的「左上角」定義為「X,Y軸」的起點「0,0」。
(這個起點的定義需要特別注意,因為跟慣常數學繪圖使用的二維軸方向邏輯不一樣,所以如果使用慣常的邏輯去試著畫圖,畫出來的東西會顛倒。)
這裡使用的繪圖法是用數學計算,所以會看到數次使用「sin」「cos」等數學運算符號/公式(?),求出一條線的起點與終點後,丟給「canvas.drawLine」這個函數指令在Canvas上畫線。
這樣的做法雖然好懂,但(可能)很吃效能。反而是Canvas工具提供「旋轉畫布」的辦法,「將畫布旋轉一定角度後,重複在同樣的起始與終點座標上畫線。」
特別注意這裡對數學運算的使用。
實務上,數學運算吃效能,所以很多時候會想辦法找投機的方式做它。
例如儘量讓圖形對稱,則一次的數學願算結果,只需要正負顛倒或相反處理後就可以產生兩次運算的結果。
如果連續重疊兩個CustomPaint,就會得到上面截圖的結果。
就不另外附上程式碼,隨有心的人自己嘗試了。