通過 Flutter 學習一下 UI 技術架構
03-28
不小心被推到前端話題了我,下面只是一些臨時的記錄而已,不推薦閱讀,打擾了
還很亂套,做個記錄先。。。
Flutter 的三層結構
- widget 層:用來定義state、props 和組件之間的關係,以及 widget 上下級關係
- element 層:更像是 widget 層的 shadow layer,element 實際上負責構成了 flutter 內部的 UI 樹
- render object 層:用於封裝 UI 底層的渲染邏輯
常見的 widget 和 element 基本上是一對一的關係,如下圖所示:

圖中 w1/w2 表示 widget,其中 w1 是 w2 的父節點。E1 和 E2 表示 Element。上下級的 Widget 之間具有 build 關係,w2 是通過調用 w1 的 build 方法創建的。
實際在界面渲染過程中,flutter 並沒有直接使用 Widget 這個數據結構來表示組件樹,而是額外用 Element 類來表示。如上圖,每個 Element 實例是通過調用 Widget 的 createElement 方法創建的,創建的 Element 保留了 Widget 實例的引用。
在渲染整個 UI 時,widget 和 element 之間的調用關係是:
- 系統先創建最上層的 w1
- 調用 w1.createElement 方法創建同級關係的 e1
- e1 再調用 w1 的 build 方法,創建 w1 的子節點 w2
- 然後在 w2 上重複步驟 1 ~ 3,逐級的將一層層的 widget 和 element 創建出來,形成整個 UI 結構
可以看到的是 Widget 上下級之間是沒有直接的引用關係的,而 Element 上下級之間是有直接引用關係的,最後的 UI 樹效果如下圖所示:
其中,圖中藍色的節點表示 Element 實例,綠色節點表示 Widget 實例。
上面介紹的 Widget 和 Element 其實是主要是封裝一些 「容器」 類組件,像 Text 等「葉子」節點類型的組件,則沒有 _child 而是有 renderObject 屬性。就以 Text 為例。創建 Text 節點的時候其實會創建以下幾個對象:
- Widget: Text 實例
- Widget: RichText 實例,作為 Text 示例的子節點
- Element: LeafRenderObjectElement 實例
- Render Object: RenderParagraph
渲染流程也和上面介紹的流程不一樣:
- Text 創建 ComponentElement 實例
- ComponentElement 實例反過來調用 Text build 方法,創建 RichText 實例,然後 ComponentElement 實例 updateChild 方法去更新 RichText 實例,這個過程會調用到 RichText 實例的 update 方法
- RichText 實例的 update 方法當中會調用 RichText 實例的 updateRenderObject 方法
- 調用 updateRenderObject 方法,而不是更新 child 節點,因為是葉子節點嘛,本來就沒有子節點了
- updateRenderObject 方法當中會更新 RenderParagraph 的屬性,比如 textAlign、text 等
- RenderParagraph 實例的屬性更新時,會將自身登記到渲染模塊的 dirty nodes 當中去
- 等 UI 樹更新完以後,渲染模塊會遍歷 dirty nodes,進行最後的 UI 渲染環節
結合上面的內容,UI 樹的結構直觀上應該是下圖這個樣子:
其中紅色的對象表示的是各種 RenderObject 子類實例
總結:
- 大部分的 Widget 和 Element 是一對一關係
- Element 實例組成了 UI 樹
- 葉子節點的 Element 實例會包含 RenderObject 實例
- RenderObject 的各種子類封裝了最終的繪製邏輯,底層用的是 canvas 介面來繪製
- 更新 UI 樹的過程中:
- widget 用來構建新的 Element 樹
- 葉子節點類型 Element 實例會包含 RenderObject 實例
- 葉子節點屬性更新時,實際更新的是 RenderObject 中的實例
- 當 Element UI 樹更新完成之後,渲染模塊會遍歷 dirty nodes 來重新布局和渲染
問題
- Text 節點是如何渲染的呢
- 首先調用 pipeline owner 的 flushLayout
- 然後調用它的 flushPaint 進行渲染
- pipeline owner 會將 dirty nodes 根據深度來進行排序,層次淺的先繪製
- 繪製過程當中會調用 RenderObject 的 paint 方法
- paint 方法當中封裝的繪製 canvas 的邏輯
- RenderParagraph 方法當中封裝的是將文本繪製到 canvas 上面的邏輯,主要是用了一個叫做 TextPainter 的模塊
推薦閱讀:
