Flutter中Widget,Element,RenderObject

三種樹

Flutter中的Widget根據(jù)類型不同,大體上可以分為以下幾種:

Widget 分類

在根Widget類中有一個抽象方法createElement(),也就意味著所有的Widget子類必須實現(xiàn)此方法:

StatelessWidget :

 StatelessElement createElement() => StatelessElement(this);

StateFulWidget :

  StatefulElement createElement() => StatefulElement(this);

SingleChildRenderObjectWidget :

 SingleChildRenderObjectElement createElement() => SingleChildRenderObjectElement(this);

MultiChildRenderObjectWidget :

MultiChildRenderObjectElement createElement() => MultiChildRenderObjectElement(this);

從源碼中可以看到,每種Widget都會調(diào)用createElement(),返回不同的Element對象.并且會傳入當(dāng)前的Widget,而Element中會引用傳過來的Widget:

  Element(Widget widget)
    : assert(widget != null),
      _widget = widget;

所以 Element 中會持有 widget

需要注意StatefulElement的構(gòu)造方法:

  StatefulElement(StatefulWidget widget)
      : _state = widget.createState(), //調(diào)用 widget 的 createState() 方法,生成 state
        super(widget) {
    assert(state._element == null);
    state._element = this;
    state._widget = widget; // 把 widget 賦值給 state 中的 widget
    assert(state._debugLifecycleState == _StateLifecycle.created);
  }

可以看到在StatefulElement的構(gòu)造方法中,會調(diào)用widgetcreateState()方法,并且會把widget賦值給state.widget,所以我們能在state中直接使用widget.

Element還有一個很關(guān)鍵的方法mount,如下:

  void mount(Element? parent, Object? newSlot) {
    assert(_lifecycleState == _ElementLifecycle.initial);
    assert(widget != null);
    assert(_parent == null);
    assert(parent == null || parent._lifecycleState == _ElementLifecycle.active);
    assert(slot == null);
    _parent = parent;
    _slot = newSlot;
    _lifecycleState = _ElementLifecycle.active;
    _depth = _parent != null ? _parent!.depth + 1 : 1;
    if (parent != null) {
      // Only assign ownership if the parent is non-null. If parent is null
      // (the root node), the owner should have already been assigned.
      // See RootRenderObjectElement.assignOwner().
      _owner = parent.owner;
    }
    assert(owner != null);
    final Key? key = widget.key;
    if (key is GlobalKey) {
      owner!._registerGlobalKey(key, this);
    }
    _updateInheritance();
  }

Flutter框架會根據(jù)Widget創(chuàng)建對應(yīng)的Element,不同的Widget創(chuàng)建的Element類型也是不一樣的,常用的Element有以下幾種:

Element

Element生成以后會調(diào)用Elementmount方法,將生成的Element掛載到Element樹上.這里的 createElementmount 都是 Flutter框架自動調(diào)用的资锰,不需要開發(fā)者手動調(diào)用敢课。因此我們平時可能沒關(guān)注這些過程阶祭。Element里面的 mount方法需要子類實現(xiàn)绷杜,我們來看看ComponentElement里的 mount方法:


  void mount(Element? parent, Object? newSlot) {
    super.mount(parent, newSlot);
    assert(_child == null);
    assert(_lifecycleState == _ElementLifecycle.active);
    _firstBuild(); //調(diào)用 _firstBuild()方法
    assert(_child != null);
  }

順著_firstBuild()查看,發(fā)現(xiàn)他們的調(diào)用鏈如下:

mount 方法調(diào)用鏈

StatelessElement 的 build()方法實現(xiàn)如下:

  Widget build() => widget.build(this);

最后實際調(diào)用的是Widgetbuild()方法;

StatefulElementbuild()方法有些不一樣:

  Widget build() => state.build(this);

StatefulElementbuild()方法實際調(diào)用的是state 的 build()方法.

小總結(jié):

創(chuàng)建 widget 時,Flutter 框架會自動調(diào)用 createElement() 生成對應(yīng)的 Element,Element 生成后會自動調(diào)用 Element 的 mount() 方法將當(dāng)前的 Element 掛載到 Element 數(shù)上.而在 mount() 方法內(nèi)部會調(diào)用 StateLessWidget 的 build() 方法 和 StateFulWidget 中 State 的 build() 方法.

現(xiàn)在我們知道了Element中的mount()方法最終都會調(diào)用build(this)方法,并且傳遞的都是當(dāng)前的Element對象,而build()方法的格式如下:

 Widget build(BuildContext context) { }

所以,我們在寫代碼時看到的BuildContext context就是Element:

上面介紹的是ComponentElement 中的 mount()方法,下面我們看看RenderObjectElement 中的 mount() 方法:

  void mount(Element? parent, Object? newSlot) {
    super.mount(parent, newSlot);
    ...
    _renderObject = widget.createRenderObject(this);
    ...
}

可以看到RenderObjectElement中的mount()方法主要調(diào)用的是Widget中的widget.createRenderObject ()方法生成 RenderObject,然后賦值給自己的 _renderObject濒募。

因此可以總結(jié):
一: ComponentElement 的 mount 方法主要作用是執(zhí)行 build
二: RenderObjectElement 的 mount 方法主要作用是執(zhí)行createRenderObject()生成 RenderObject
Widget中還有一個很重要的方法static bool canUpdate():
 static bool canUpdate(Widget oldWidget, Widget newWidget) {
    return oldWidget.runtimeType == newWidget.runtimeType
        && oldWidget.key == newWidget.key;
  }

為了進行Element復(fù)用鞭盟,在Element重新構(gòu)建前會先嘗試是否可以復(fù)用舊樹上相同位置的element,element節(jié)點在更新前都會調(diào)用其對應(yīng)Widget的canUpdate方法瑰剃,主要是判斷newWidget與oldWidget的runtimeType和key是否同時相等齿诉,如果同時相等就返回true,表示復(fù)用舊Element,否則返回false粤剧,重新創(chuàng)建歇竟。

寫這篇文章是為了方便自己復(fù)習(xí),參考了這邊文章,更詳細的請查看Flutter渲染之Widget、Element 和 RenderObject

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末抵恋,一起剝皮案震驚了整個濱河市焕议,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌弧关,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,270評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異草描,居然都是意外死亡懂算,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,489評論 3 395
  • 文/潘曉璐 我一進店門株憾,熙熙樓的掌柜王于貴愁眉苦臉地迎上來蝙寨,“玉大人,你說我怎么就攤上這事嗤瞎∽崖” “怎么了?”我有些...
    開封第一講書人閱讀 165,630評論 0 356
  • 文/不壞的土叔 我叫張陵猫胁,是天一觀的道長箱亿。 經(jīng)常有香客問我,道長弃秆,這世上最難降的妖魔是什么届惋? 我笑而不...
    開封第一講書人閱讀 58,906評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮菠赚,結(jié)果婚禮上脑豹,老公的妹妹穿的比我還像新娘。我一直安慰自己衡查,他們只是感情好瘩欺,可當(dāng)我...
    茶點故事閱讀 67,928評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著拌牲,像睡著了一般俱饿。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上塌忽,一...
    開封第一講書人閱讀 51,718評論 1 305
  • 那天拍埠,我揣著相機與錄音,去河邊找鬼土居。 笑死枣购,一個胖子當(dāng)著我的面吹牛嬉探,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播棉圈,決...
    沈念sama閱讀 40,442評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼涩堤,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了分瘾?” 一聲冷哼從身側(cè)響起定躏,我...
    開封第一講書人閱讀 39,345評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎芹敌,沒想到半個月后痊远,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,802評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡氏捞,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,984評論 3 337
  • 正文 我和宋清朗相戀三年碧聪,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片液茎。...
    茶點故事閱讀 40,117評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡逞姿,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出捆等,到底是詐尸還是另有隱情滞造,我是刑警寧澤,帶...
    沈念sama閱讀 35,810評論 5 346
  • 正文 年R本政府宣布栋烤,位于F島的核電站谒养,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏明郭。R本人自食惡果不足惜买窟,卻給世界環(huán)境...
    茶點故事閱讀 41,462評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望薯定。 院中可真熱鬧始绍,春花似錦、人聲如沸话侄。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,011評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽年堆。三九已至吞杭,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間嘀韧,已是汗流浹背篇亭。 一陣腳步聲響...
    開封第一講書人閱讀 33,139評論 1 272
  • 我被黑心中介騙來泰國打工缠捌, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留锄贷,地道東北人译蒂。 一個月前我還...
    沈念sama閱讀 48,377評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像谊却,于是被迫代替她去往敵國和親柔昼。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,060評論 2 355

推薦閱讀更多精彩內(nèi)容