Flutter完整開發實戰詳解(6、 深刻Widget原理)

做爲系列文章的第六篇,本篇主要在前文的探索下,針對描述一下 Widget 中的一些有意思的原理。git

前文:github

首先咱們須要明白,Widget 是什麼?這裏有一個 「總所周知」 的答就是:Widget並不真正的渲染對象 。是的,事實上在 Flutter 中渲染是經歷了從 WidgetElement 再到 RenderObject 的過程。bash

咱們都知道 Widget 是不可變的,那麼 Widget 是如何在不可變中去構建畫面的?上面咱們知道,Widget 是須要轉化爲 Element 去渲染的,而從下圖註釋能夠看到,事實上 Widget 只是 Element 的一個配置描述 ,告訴 Element 這個實例如何去渲染。佈局

那麼 Widget 和 Element 之間是怎樣的對應關係呢?從上圖註釋也可知: Widget 和 Element 之間是一對多的關係 。實際上渲染樹是由 Element 實例的節點構成的樹,而做爲配置文件的 Widget 可能被複用到樹的多個部分,對應產生多個 Element 對象。post

那麼RenderObject 又是什麼?它和上述兩個的關係是什麼?從源碼註釋寫着 An object in the render tree 能夠看出到 RenderObject 纔是實際的渲染對象,而經過 Element 源碼咱們能夠看出:Element 持有 RenderObject 和 Widget。優化

再結合下圖,能夠大體總結出三者的關係是:配置文件 Widget 生成了 Element,然後建立 RenderObject 關聯到 Element 的內部 renderObject 對象上,最後Flutter 經過 RenderObject 數據來佈局和繪製。 理論上你也能夠認爲 RenderObject 是最終給 Flutter 的渲染數據,它保存了大小和位置等信息,Flutter 經過它去繪製出畫面。ui

說到 RenderObject ,就不得不說 RenderBoxA render object in a 2D Cartesian coordinate system,從源碼註釋能夠看出,它是在繼承 RenderObject 基礎的佈局和繪製功能上,實現了「笛卡爾座標系」:以 Top、Left 爲基點,經過寬高兩個軸實現佈局和嵌套的。spa

RenderBox 避免了直接使用 RenderObject 的麻煩場景,其中 RenderBox 的佈局和計算大小是在 performLayout()performResize() 這兩個方法中去處理,不少時候咱們更多的是選擇繼承 RenderBox 去實現自定義。code

綜合上述狀況,咱們知道:orm

  • Widget只是顯示的數據配置,因此相對而言是輕量級的存在,而 Flutter 中對 Widget 的也作了必定的優化,因此每次改變狀態致使的 Widget 重構並不會有太大的問題。
  • RenderObject 就不一樣了,RenderObject 涉及到佈局、計算、繪製等流程,要是每次都所有從新建立開銷就比較大了。

因此針對是否每次都須要建立出新的 Element 和 RenderObject 對象,Widget 都作了對應的判斷以便於複用,好比:在 newWidgetoldWidgetruntimeTypekey 相等時會選擇使用 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));
  }

複製代碼

--

自此,第六篇終於結束了!(///▽///)

資源推薦

完整開源項目推薦:
文章

《Flutter完整開發實戰詳解(1、Dart語言和Flutter基礎)》

《Flutter完整開發實戰詳解(2、 快速開發實戰篇)》

《Flutter完整開發實戰詳解(3、 打包與填坑篇)》

《Flutter完整開發實戰詳解(4、Redux、主題、國際化)》

《Flutter完整開發實戰詳解(5、 深刻探索)》

《Flutter完整開發實戰詳解(6、 深刻Widget原理)》

《Flutter完整開發實戰詳解(7、 深刻佈局原理)》

《Flutter完整開發實戰詳解(8、 實用技巧與填坑)》

《Flutter完整開發實戰詳解(9、 深刻繪製原理)》

《Flutter完整開發實戰詳解(10、 深刻圖片加載流程)》

《Flutter完整開發實戰詳解(11、全面深刻理解Stream)》

《跨平臺項目開源項目推薦》

《移動端跨平臺開發的深度解析》

《React Native 的將來與React Hooks》

咱們還會再見嗎?
相關文章
相關標籤/搜索