Flutter 入門 - Widget -- Element -- RenderObject

這裡沒有賀先生發表於2021-08-26

Widget

為什麼需要自己寫 build 方法

  • 某些 widget 會生成 RenderObject
  • 每一個 Widget 都會生成 Element 例項
  • 生成 Element 例項的時候會呼叫 mount 方法
  • statfulWidget、statelessWidgetextentsComponentElement
  • ComponentElement: mount -> _firstBuild -> rebuild -> performRebuild -> build
  • RenderObjectElement: mount -> _widget.createRenderObject

元件Widget

statfulWidget、statelessWidgetextentsComponentElement 不會生成 RenderObject

  • Container
  • Text
  • 自己手寫的 Widget

statfulWidget

如果是一個StatefulWidget,則建立出來的是一個StatefulElement

我們來看一下StatefulElement的構造器:

  • 呼叫widget的createState()
  • 所以StatefulElement對建立出來的State是有一個引用的
  • 而_state又對widget有一個引用
  StatefulElement(StatefulWidget widget)
      : _state = widget.createState(),
  ....省略程式碼
  _state._widget = widget;
複製程式碼

而呼叫build的時候,本質上呼叫的是_state中的build方法:

Widget build() => state.build(this);
複製程式碼

建立流程

ComponentElement: mount -> _firstBuild -> rebuild -> performRebuild -> build

非元件 Widget

Padding

  • 建立Element: RenderObjectElement
  • RenderObjectElement: mount -> _widget.createRenderObject
  • 渲染Widget: 生成RenderObject

Widget 繼承鏈

Padding -> SingleChildRenderObjectWidget -> RenderObjectWidget -> Widget

Element 繼承

RenderPadding -> RenderShiftedBox -> RenderBox -> RenderObject

Element

大量的 widget 被重複建立和銷燬,屬於不穩定的,那誰來維持整個程式的渲染穩定呢,Element! Element 是 Widget的例項,是在樹中詳細的位置

在每一次建立Widget的時候,會建立一個對應的Element,然後將該元素插入樹中。

  • Element儲存著對Widget的引用;

在SingleChildRenderObjectWidget中,我們可以找到如下程式碼:

  • 在Widget中,Element被建立,並且在建立時,將this(Widget)傳入了;
  • Element就儲存了對Widget的應用;
@override
  SingleChildRenderObjectElement createElement() => SingleChildRenderObjectElement(this);
複製程式碼

在建立完一個Element之後,Framework會呼叫mount方法來將Element插入到樹中具體的位置:

圖片

mount方法

在呼叫mount方法時,會同時使用Widget來建立RenderObject,並且保持對RenderObject的引用:

  • _renderObject = widget.createRenderObject(this);
@override
  void mount(Element parent, dynamic newSlot) {
    super.mount(parent, newSlot);
    _renderObject = widget.createRenderObject(this);
    assert(() {
      _debugUpdateRenderObjectOwner();
      returntrue;
    }());
    assert(_slot == newSlot);
    attachRenderObject(newSlot);
    _dirty = false;
  }
複製程式碼

但是,如果你去看類似於Text這種組合類的Widget,它也會執行mount方法,但是mount方法中並沒有呼叫createRenderObject這樣的方法。

  • 我們發現ComponentElement最主要的目的是掛載之後,呼叫_firstBuild方法
@override
  void mount(Element parent, dynamic newSlot) {
    super.mount(parent, newSlot);
    assert(_child == null);
    assert(_active);
    _firstBuild();
    assert(_child != null);
  }
​
  void _firstBuild() {
    rebuild();
  }
複製程式碼

RenderObject

建立過程小結

Widget只是描述了配置資訊:

  • 其中包含createElement方法用於建立Element
  • 也包含createRenderObject,但是不是自己在呼叫

Element是真正儲存樹結構的物件:

  • 建立出來後會由framework呼叫mount方法;
  • 在mount方法中會呼叫widget的createRenderObject物件;
  • 並且Element對widget和RenderObject都有引用;

RenderObject是真正渲染的物件:

  • 其中有markNeedsLayout performLayout markNeedsPaint paint等方法

參考

Flutter的Widget-Element-RenderObject

相關文章