咱們在學習 Flutter 的時候,可能常常看到三個名詞: Widget、RenderObject 和 Element ,弄懂這幾個概念可能也是入門 Flutter 框架原理的第一步。編程
在 Flutter 中,萬物皆是 Widget,不管是可見的仍是功能型的,那麼 Widget 到底是什麼呢?bash
按照慣例,先看官方文檔。微信
接下來看一下 Widget 的定義。框架
abstract class Widget {
const Widget({ this.key });
final Key key;
@protected
Element createElement();
static bool canUpdate(Widget oldWidget, Widget newWidget) {
return oldWidget.runtimeType == newWidget.runtimeType
&& oldWidget.key == newWidget.key;
}
}
複製代碼
經過上面 Widget 的定義,能夠看到有兩個重要的方法,一個是經過 createElement 來建立 Element 對象的,一個是根據 key 來決定更新行爲的 canUpdate 方法。ide
以 Opacity 爲例,Opacity 作爲一個 Widget ,只保存另外一個配置信息:opacity,這個屬性決定了透明度,範圍在 0 到 1 之間。函數
Opacity 既然作爲一個 Widget,確定是 Widget 的子類,其繼承關係以下:佈局
Opacity → SingleChildRenderObjectWidget → RenderObjectWidget → Widget
複製代碼
Opacity 的定義以下:學習
class Opacity extends SingleChildRenderObjectWidget {
const Opacity({
Key key,
@required this.opacity,
this.alwaysIncludeSemantics = false,
Widget child,
}) : assert(opacity != null && opacity >= 0.0 && opacity <= 1.0),
assert(alwaysIncludeSemantics != null),
super(key: key, child: child);
final double opacity;
@override
RenderOpacity createRenderObject(BuildContext context) {
return RenderOpacity(
opacity: opacity,
alwaysIncludeSemantics: alwaysIncludeSemantics,
);
}
@override
void updateRenderObject(BuildContext context, RenderOpacity renderObject) {
renderObject
..opacity = opacity
..alwaysIncludeSemantics = alwaysIncludeSemantics;
}
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties.add(DoubleProperty('opacity', opacity));
properties.add(FlagProperty('alwaysIncludeSemantics', value: alwaysIncludeSemantics, ifTrue: 'alwaysIncludeSemantics'));
}
}
複製代碼
經過上面的代碼,能夠看到 opacity 是 final 類型的屬性,只能作爲構造函數參數傳遞進去,不可改變,所以若是要更新這個屬性,必須新建一個 Opcity 對象,這也是爲何咱們代碼裏的 Widget build(BuildContext context) 方法裏面每次 build 都會建立新的對象實例的緣由。ui
RenderObject 的定義以下this
abstract class RenderObject extends AbstractNode with DiagnosticableTreeMixin implements HitTestTarget {
ParentData parentData;
Constraints _constraints;
void layout(Constraints constraints, { bool parentUsesSize = false }) {
}
void paint(PaintingContext context, Offset offset) { }
void performLayout();
void markNeedsPaint() {
}
}
複製代碼
經過以上定義,能夠看出,RenderObject 的主要做用就是繪製和佈局的。
那麼這個 RenderObject 是哪裏來的呢?是在 Widget 裏面建立的。如上面的 Opacity 中重寫了 createRenderObject 方法來建立 RenderOpacity 對象。
@override
RenderOpacity createRenderObject(BuildContext context) {
return RenderOpacity(
opacity: opacity,
alwaysIncludeSemantics: alwaysIncludeSemantics,
);
}
複製代碼
在 RenderOpacity 內部實現了佈局、點擊檢測和大小計算等功能。
Element 的 生命週期:
Flutter framework 經過 Widget.createElement 來建立一個 element 。
每當 Widget 建立並插入到 Widget 樹中時,framework 就會經過 mount 方法來把這個 widget 建立並關聯的 element 插入到 element 樹中(其父 element 會給出一個位置)。
經過 attachRenderObject 方法來將 render objects 來關聯到 render 樹上,這時能夠認爲這個 widget 已經顯示在屏幕上了。
每當執行了 rebuid 方法,widget 表明的配置信息改變時(建立了一個新的 widget),framewrok 就會調用這個新的 widget 的 update 方法(新的 widget 的 和老的 widget 有相同的 runtimeType 和 key,若是不一樣,就要先 unmounting 而後從新裝載 widget)。
當 element 的祖先想要移除一個子 element 時,能夠經過 deactivateChild 方法,先把這個 element 從 樹中移除,而後將這個 element 加入到一個「不活躍元素列表」中,接着 framework 就會將這個 element 從屏幕移除(當下一個渲染幀到來這個 element 依然不活躍)。
因爲 是在 Widget 中建立了Element,相似於 Widget 的繼承關係,Element 的繼承關係以下:
SingleChildRenderObjectElement → RenderObjectElement → Element
複製代碼
接着看一下 Opacity 中,如何建立一個 Element 。 Opacity 繼承自 SingleChildRenderObjectElement,在 SingleChildRenderObject 中建立了 Element
@override
SingleChildRenderObjectElement createElement() => new SingleChildRenderObjectElement(this);
複製代碼
在 RenderObjectElement 中提供了 mount 方法
abstract class RenderObjectElement extends Element {
RenderObjectElement(RenderObjectWidget widget) : super(widget);
RenderObject _renderObject;
@override
void mount(Element parent, dynamic newSlot) {
super.mount(parent, newSlot);
_renderObject = widget.createRenderObject(this);
attachRenderObject(newSlot);
_dirty = false;
}
}
複製代碼
經過上面的代碼,咱們可以發現,Element 中經過 widget.createRenderObject 方法也拿到了 RenderObject 對象,所以 Element 實際上是同時包含 RenderObject 和 Widget 。
mount 方法會將 element 插入到 element 樹中,mount 中還會調用 attachRenderObject 方法。
abstract class RenderObjectElement extends Element {
@override
void attachRenderObject(dynamic newSlot) {
_slot = newSlot;
_ancestorRenderObjectElement = _findAncestorRenderObjectElement();
_ancestorRenderObjectElement?.insertChildRenderObject(renderObject, newSlot);
if (parentDataElement != null)
_updateParentData(parentDataElement.widget);
}
}
複製代碼
在這個方法裏,經過 _findAncestorRenderObjectElement 方法, 找到了Element樹上的祖先Element,若是祖先不爲空,就調用insertChildRenderObject方法,這個方法的意思就是把renderObject的child替換成newSlot,而後經過 _updateParentData 用於更新佈局數據的一些信息。
上面只是簡單介紹了一下 Flutter 中的 Widget 、RenderObject 和 Element 中的概念,而 Widget,Element和RenderObject體系是Flutter框架的核心 至於內部原理以及若是工做的,須要結合 Flutter 框架結構運行原理來看,這樣才能更好的理解這些概念。
參考:
Flutter, what are Widgets, RenderObjects and Elements