Flutter - 從 runApp 方法開始,聊一聊 Render

語言: CN / TW / HK

theme: juejin

順著程式碼看:

入口函式 main

Dart 是從 main 方法開始的,通過 runApp 方法把做為 App 的 Widget 構建出來:

image.png

runApp 方法

image.png 可以注意到,runApp 方法會初始化一個 WidgetsFlutterBinding 物件,然後通過 scheduleAttachRootWidget 方法,把 App 小元件安排到了根小元件上。

attachRootWidget 方法

image.png 可以發現我們的 App 被當作成 rootWidget,在 attachRootWidget 方法中,通過 RenderObjectToWidgetAdapter 這個 Widget,把 App 作為 child 來構建它,然後執行了它的 attachToRenderTree 方法。由於 renderViewElement 是跟隨 WidgetsFlutterBinding 一同被構建的,而 renderViewElement 還沒有被例項化,所以傳進方法內的值為 null。

attachToRenderTree 方法

image.png elementnull,則會依次執行 createElementmount 方法。

createElement 方法

image.png createElement 會例項化一個叫做 RenderObjectToWidgetElementElement,並把自己(RenderObjectToWidgetAdapter)作為 property 傳入。

mount 方法

image.png mount 可以理解為安裝,可以看到第 5662 行,剛才作為 property 傳入的 widgetRenderObjectToWidgetAdapter)執行了 createRenderObject 方法,並且把自己(RenderObjectToWidgetElement)作為 property 傳進方法裡。

看到這裡,WidgetElementRenderObject 這三個大名鼎鼎的關鍵詞都已經出現了,但他們的關係好亂,我們繼續看:

createRenderObject 方法

image.png

image.png 通過 RenderObjectToWidgetAdaptercreateRenderObject 方法後,我們得到了 RenderObject。這個 RenderObject 是什麼?通過 1099 行,再到 1090 行,再到 1080 行,我們返回到初始化 RenderObjectToWidgetAdapter 的地方,看看第二個引數是什麼:

image.png 所以繞了一大圈,我們知道了,通過 createRenderObject 方法拿到的是隨著 WidgetsFlutterBinding 一同產生的一個叫 renderViewRenderObject

_rebuild 方法

剛才的 mount 方法我們才看了一半,接下來的第 1171 行,執行了 _rebuild 方法: image.png

image.png 這個方法裡執行了一個叫做 updateChild 的方法,這個方法的第二個引數 (widget as RenderObjectToWidgetAdapter<T>).child,就是我們最開始傳入的 App。

updateChild 方法

我們來看看 updateChild 方法是怎麼說的:

image.png 這個可是尤其重要的一個方法。

inflateWidget 方法

接下來會進到 inflateWidget 方法:

image.png

image.png inflateWidget 方法裡會用我的 App 來執行 createElement 方法,因為我的 App 是繼承的 StatelessWidget,所以 createElement 方法會建立一個 StatelessElement 並把自己(StatelessWidget)當成 property 傳進 StatelessElement 內來使之構造。所以在這裡 newChildStatelessElement,接著第 3817 行會將 StatelessElement 執行 mount 方法,把 thisRenderView)傳進方法裡。上面說到了,mount 可以理解為安裝,所以這行方法等於是讓 StatelessElement 拿著 RenderObjectRenderView)。

執行 mount 方法,又回到了這裡:

image.png

attachRenderObject 方法

接著執行 attachRenderObject方法:

image.png 該方法內,_findAncestorRenderObjectElement 顧名思義,查詢祖先的 RenderObjectElement,最上面最上面我們還記得,祖先 RenderObjectElementRenderObjectToWidgetElement

再進入 RenderObjectToWidgetElementinsertRenderObjectChild 方法:

image.png 我們來看第 1223 行。

renderObject 是誰呢?renderObjectWidgetsFlutterBindingrenderView

child 是誰呢?是我們 App 的 RenderObject,也就是 StatelessElement 拿著的那個 RenderObject

到這裡,終於把 App 的 RenderObjectWidgetsFlutterBindingRenderObject 綁在了一起。

小結一下:

  1. 塞了一個 WidgetrunApp
  2. 例項化了一個附帶 RenderObjectrenderView)的 WidgetsFlutterBinding
  3. 例項化了一個 RenderObjectToWidgetAdapter
  4. 利用 RenderObjectToWidgetAdapter 例項化了一個 RenderObjectToWidgetElement 並將RenderObjectToWidgetAdapter 作為 RenderObjectToWidgetElementproperty
  5. RenderObjectToWidgetElement 會通過 createElement 產生傳入 runAppWidgetElement
  6. 利用 Widget 產生的 Element 通過 Widget 產生 RenderObject
  7. 利用 RenderObjectToWidgetElementWidgetRenderObject 變成 WidgetsFlutterBindingrenderViewchild,並將其作為 root widget

所以我們瞭解到, RenderObject 是通過 Element 利用 Widget 產生出來的,而 Element 是由 Widget 產生出來的。 image.png

做完以上,接著通過 scheduleWarmUpFrame 就會把 widget 提供的配置資訊 render 在手機螢幕上了。

RenderObject 到底是個啥

待續

Widget、Element、RenderObject 三者的關係

待續