當(dāng)Flutter提供的現(xiàn)有組件無法滿足我們的需求,或者我們?yōu)榱斯蚕泶a需要封裝一些通用組件,這時我們就需要自定義組件。在Flutter中自定義組件有三種方式:通過組合其它組件、自繪和實現(xiàn)RenderObject。
1. 組合多個Widget
這種方式是通過拼裝多個組件來組合成一個新的組件。例如我們之前介紹的Container就是一個組合組件,它是由DecoratedBox、ConstrainedBox、Transform、Padding、Align等組件組成。
在Flutter中,組合的思想非常重要,F(xiàn)lutter提供了非常多的基礎(chǔ)組件,而我們的界面開發(fā)其實就是按照需要組合這些組件來實現(xiàn)各種不同的布局而已。
2. 通過CustomPaint自繪
如果遇到無法通過現(xiàn)有的組件來實現(xiàn)需要的UI時,我們可以通過自繪組件的方式來實現(xiàn),例如我們需要一個顏色漸變的圓形進度條,而Flutter提供的CircularProgressIndicator并不支持在顯示精確進度時對進度條應(yīng)用漸變色(其valueColor 屬性只支持執(zhí)行旋轉(zhuǎn)動畫時變化Indicator的顏色),這時最好的方法就是通過自定義組件來繪制出我們期望的外觀。我們可以通過Flutter中提供的CustomPaint和Canvas來實現(xiàn)UI自繪。
3. 通過RenderObject自繪
Flutter提供的自身具有UI外觀的組件,如文本Text、Image都是通過相應(yīng)的RenderObject渲染出來的,如Text是由RenderParagraph渲染;而Image是由RenderImage渲染。RenderObject是一個抽象類,它定義了一個抽象方法paint(...):
void paint(PaintingContext context, Offset offset)
PaintingContext代表組件的繪制上下文,通過PaintingContext.canvas可以獲得Canvas,而繪制邏輯主要是通過Canvas API來實現(xiàn)。子類需要重寫此方法以實現(xiàn)自身的繪制邏輯,如RenderParagraph需要實現(xiàn)文本繪制邏輯,而RenderImage需要實現(xiàn)圖片繪制邏輯。
可以發(fā)現(xiàn),RenderObject中最終也是通過Canvas API來繪制的,那么通過實現(xiàn)RenderObject的方式和上面介紹的通過CustomPaint和Canvas自繪的方式有什么區(qū)別?其實答案很簡單,CustomPaint只是為了方便開發(fā)者封裝的一個代理類,它直接繼承自SingleChildRenderObjectWidget,通過RenderCustomPaint的paint方法將Canvas和畫筆Painter(需要開發(fā)者實現(xiàn))連接起來實現(xiàn)了最終的繪制(繪制邏輯在Painter中)。
4. 總結(jié)
“組合”是自定義組件最簡單的方法,在任何需要自定義組件的場景下,我們都應(yīng)該優(yōu)先考慮是否能夠通過組合來實現(xiàn)。而通過CustomPaint和RenderObject自繪的方式本質(zhì)上是一樣的,都需要開發(fā)者調(diào)用Canvas API手動去繪制UI,優(yōu)點是強大靈活,理論上可以實現(xiàn)任何外觀的UI,而缺點是必須了解Canvas API細節(jié),并且得自己去實現(xiàn)繪制邏輯