Widget
為什麼需要自己寫 build 方法
- 某些 widget 會生成 RenderObject
- 每一個 Widget 都會生成 Element 例項
- 生成 Element 例項的時候會呼叫 mount 方法
- statfulWidget、statelessWidget
extents
ComponentElement - ComponentElement: mount -> _firstBuild -> rebuild -> performRebuild -> build
- RenderObjectElement: mount -> _widget.createRenderObject
元件Widget
statfulWidget、statelessWidgetextents
ComponentElement 不會生成 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
等方法