作為系列文章的第六篇,本篇主要在前文的探索下,針對描述一下 Widget 中的一些有意思的原理。
前文:
首先我們需要明白,Widget 是什麼?這裡有一個 “總所周知” 的答就是:Widget並不真正的渲染物件 。是的,事實上在 Flutter 中渲染是經歷了從 Widget
到 Element
再到 RenderObject
的過程。
我們都知道 Widget 是不可變的,那麼 Widget 是如何在不可變中去構建畫面的?上面我們知道,Widget
是需要轉化為 Element
去渲染的,而從下圖註釋可以看到,事實上 Widget 只是 Element 的一個配置描述 ,告訴 Element 這個例項如何去渲染。
那麼 Widget 和 Element 之間是怎樣的對應關係呢?從上圖註釋也可知: Widget 和 Element 之間是一對多的關係 。實際上渲染樹是由 Element 例項的節點構成的樹,而作為配置檔案的 Widget 可能被複用到樹的多個部分,對應產生多個 Element 物件。
那麼RenderObject
又是什麼?它和上述兩個的關係是什麼?從原始碼註釋寫著 An object in the render tree
可以看出到 RenderObject
才是實際的渲染物件,而通過 Element 原始碼我們可以看出:Element 持有 RenderObject 和 Widget。
再結合下圖,可以大致總結出三者的關係是:配置檔案 Widget 生成了 Element,而後建立 RenderObject 關聯到 Element 的內部 renderObject
物件上,最後Flutter 通過 RenderObject 資料來佈局和繪製。 理論上你也可以認為 RenderObject 是最終給 Flutter 的渲染資料,它儲存了大小和位置等資訊,Flutter 通過它去繪製出畫面。
說到 RenderObject
,就不得不說 RenderBox
:A render object in a 2D Cartesian coordinate system
,從原始碼註釋可以看出,它是在繼承 RenderObject
基礎的佈局和繪製功能上,實現了“笛卡爾座標系”:以 Top、Left 為基點,通過寬高兩個軸實現佈局和巢狀的。
RenderBox 避免了直接使用 RenderObject
的麻煩場景,其中 RenderBox
的佈局和計算大小是在 performLayout()
和 performResize()
這兩個方法中去處理,很多時候我們更多的是選擇繼承 RenderBox
去實現自定義。
綜合上述情況,我們知道:
- Widget只是顯示的資料配置,所以相對而言是輕量級的存在,而 Flutter 中對 Widget 的也做了一定的優化,所以每次改變狀態導致的 Widget 重構並不會有太大的問題。
- RenderObject 就不同了,RenderObject 涉及到佈局、計算、繪製等流程,要是每次都全部重新建立開銷就比較大了。
所以針對是否每次都需要建立出新的 Element 和 RenderObject 物件,Widget 都做了對應的判斷以便於複用,比如:在 newWidget
與oldWidget
的 runtimeType 和 key 相等時會選擇使用 newWidget
去更新已經存在的 Element 物件,不然就選擇重新建立新的 Element。
由此可知:Widget 重新建立,Element 樹和 RenderObject 樹並不會完全重新建立。
看到這,說個題外話:那一般我們可以怎麼獲取佈局的大小和位置呢?
首先這裡需要用到我們前文中提過的 GlobalKey
,通過 key 去獲取到控制元件物件的 BuildContext
,而我們也知道 BuildContext
的實現其實是 Element
,而Element
持有 RenderObject
。So,我們知道的 RenderObject
,實際上獲取到的就是 RenderBox
,那麼通過 RenderBox
我們就只大小和位置了。
showSizes() {
RenderBox renderBoxRed = fileListKey.currentContext.findRenderObject();
print(renderBoxRed.size);
}
showPositions() {
RenderBox renderBoxRed = fileListKey.currentContext.findRenderObject();
print(renderBoxRed.localToGlobal(Offset.zero));
}
複製程式碼
--
自此,第六篇終於結束了!(///▽///)
資源推薦
- Github : https://github.com/CarGuo
- 本文程式碼 :https://github.com/CarGuo/GSYGithubAppFlutter
完整開源專案推薦:
文章
《Flutter完整開發實戰詳解(一、Dart語言和Flutter基礎)》