# 自定義Widget方法簡介
當Flutter提供的現有Widget無法滿足我們的需求,或者我們為了共享代碼需要封裝一些通用Widget,這時我們就需要自定義Widget。在Flutter中自定義Widget有三種方式:通過組合其它Widget、自繪和實現RenderObject,本節我們先分別介紹一下這三種方式的特點,后面章節中則詳細介紹它們的細節。
### 組合其它Widget
這種方式是通過拼裝其它低級別的Widget來組合成一個高級別的Widget,例如我們之前介紹的Container就是一個組合Widget,它是由DecoratedBox、ConstrainedBox、Transform、Padding、Align等組成。
在Flutter中,組合的思想非常重要,Flutter提供了非常多的基礎Widget,而我們的界面開發都是按照需要組合這些Widget來實現各種不同的布局。
### 自繪
如果遇到無法通過系統提供的現有Widget實現的UI時,如我們需要一個漸變圓形進度條,而Flutter提供的CircularProgressIndicator并不支持在顯示精確進度時對進度條應用漸變色(其`valueColor` 屬性只支持執行旋轉動畫時變化Indicator的顏色),這時最好的方法就是通過自定義Widget繪制邏輯來畫出我們期望的外觀。Flutter中提供了CustomPaint和Canvas供我們自繪UI外觀。
### 實現RenderObject
Flutter提供的任何具有UI外觀的Widget,如文本Text、Image都是通過相應的RenderObject渲染出來的,如Text是由RenderParagraph渲染,而Image是由RenderImage渲染。RenderObject是一個抽象類,它定義了一個抽象方法`paint(...)`:
```
void paint(PaintingContext context, Offset offset)
```
PaintingContext代表Widget的繪制上下文,通過`PaintingContext.canvas`可以獲得Canvas,繪制邏輯主要是通過Canvas API來實現。子類需要實現此方法以實現自身的繪制邏輯,如RenderParagraph需要實現文本繪制邏輯,而RenderImage需要實現圖片繪制邏輯。
可以發現,RenderObject中最終也是通過Canvas來繪制的,那么通過實現RenderObject的方式和上面介紹的通過CustomPaint和Canvas自繪的方式有什么區別?其實答案很簡單,CustomPaint只是為了方便開發者封裝的一個代理類,它直接繼承自SingleChildRenderObjectWidget,通過RenderCustomPaint的paint方法將Canvas和畫筆Painter(需要開發者實現,后面章節介紹)連接起來實現了最終的繪制(繪制邏輯在Painter中)。
### 總結
組合是自定義組件最簡單的方法,在任何需要自定義的場景下,都應該優先考慮是否能夠通過組合來實現。而自繪和通過實現RenderObject的方法本質上是一樣的,都需要開發者調用Canvas API手動去繪制UI,缺點是必須了解Canvas API,并且得自己去實現繪制邏輯,而優點是強大靈活,理論上可以實現任何外觀的UI。
在本章接下來的小節中,我們將通過一些實例來詳細介紹自定義UI的過程,由于后兩種方法本質是相同的,后續我們只介紹CustomPaint和Canvas的方式,讀者如果對自定義RenderObject的方法好奇,可以查看RenderParagraph或RenderImage源碼。
- 緣起
- 起步
- 移動開發技術簡介
- Flutter簡介
- 搭建Flutter開發環境
- 常見配置問題
- Dart語言簡介
- 第一個Flutter應用
- 計數器示例
- 路由管理
- 包管理
- 資源管理
- 調試Flutter APP
- Dart線程模型及異常捕獲
- 基礎Widgets
- Widget簡介
- 文本、字體樣式
- 按鈕
- 圖片和Icon
- 單選框和復選框
- 輸入框和表單
- 布局類Widgets
- 布局類Widgets簡介
- 線性布局Row、Column
- 彈性布局Flex
- 流式布局Wrap、Flow
- 層疊布局Stack、Positioned
- 容器類Widgets
- Padding
- 布局限制類容器ConstrainedBox、SizeBox
- 裝飾容器DecoratedBox
- 變換Transform
- Container容器
- Scaffold、TabBar、底部導航
- 可滾動Widgets
- 可滾動Widgets簡介
- SingleChildScrollView
- ListView
- GridView
- CustomScrollView
- 滾動監聽及控制ScrollController
- 功能型Widgets
- 導航返回攔截-WillPopScope
- 數據共享-InheritedWidget
- 主題-Theme
- 事件處理與通知
- 原始指針事件處理
- 手勢識別
- 全局事件總線
- 通知Notification
- 動畫
- Flutter動畫簡介
- 動畫結構
- 自定義路由過渡動畫
- Hero動畫
- 交錯動畫
- 自定義Widget
- 自定義Widget方法簡介
- 通過組合現有Widget實現
- 實例:TurnBox
- CustomPaint與Canvas
- 實例:圓形漸變進度條(自繪)
- 文件操作與網絡請求
- 文件操作
- Http請求-HttpClient
- Http請求-Dio package
- 實例:Http分塊下載
- WebSocket
- 使用Socket API
- Json轉Model
- 包與插件
- 開發package
- 插件開發:平臺通道簡介
- 插件開發:實現Android端API
- 插件開發:實現IOS端API
- 系統能力調用
- 國際化
- 讓App支持多語言
- 實現Localizations
- 使用Intl包
- Flutter核心原理
- Flutter UI系統
- Element和BuildContext
- RenderObject與RenderBox
- Flutter從啟動到顯示