Flutter中的Element(下篇)

級別: ★☆☆☆☆
標(biāo)簽:「Flutter」「Element 」「重要方法與調(diào)用時機(jī) 」
作者: 沐靈洛
審校: QiShare團(tuán)隊


上一篇我們簡單介紹了Flutter中Element是什么以及它的生命周期措拇。
本篇我們將與大家一起探討Element的一些重要的方法的作用以及調(diào)用時機(jī)。

Element的重要方法

  • void mount(Element parent, dynamic newSlot)

描述:parent element中給定的插槽上废恋,加入當(dāng)前的 element矢空。
調(diào)用時機(jī):新創(chuàng)建的element首次加入樹中時,

  • Element updateChild(Element child, Widget newWidget, dynamic newSlot)

描述:使用新的配置widget,更新樹中element棋恼。這個方法是widges系統(tǒng)的核心方法。

調(diào)用時機(jī):每次基于更新后的widget,添加邦邦、更新或移除樹中的child element時安吁。

方法原理:

newWidget == null newWidget != null
child == null Returns null. Returns new [Element].
child != null Old child is removed, returns null. Old child updated if possible, returns child or new [Element].
Element updateChild(Element child, Widget newWidget, dynamic newSlot) {                               
  if (newWidget == null) {                                                                                      
    if (child != null)     
      ///newWidget== null 棄用element                                                                                     
      deactivateChild(child);                                                                                   
    return null;                                                                                                
  }                                                                                                             
  Element newChild;                                                                                             
  if (child != null) {            
   ///標(biāo)志位:解決hot reload前后Widget的類型不一致導(dǎo)致的類型錯誤  。
  ///比如:之前是`StatefulWidget`(StatefulElement) ,現(xiàn)在是`StatelessWidget`(StatelessElement)  燃辖。                                                          
    bool hasSameSuperclass = true;                                         
    assert(() {
     ///element is StatefulElement ? 1 :  element is StatelessElement ? 2 : 0;                                                                                                                                 
      final int oldElementClass = Element._debugConcreteSubtype(child);  
     /// widget is StatefulWidget ? 1 : widget is StatelessWidget ? 2 :  0;                                                                          
      final int newWidgetClass = Widget._debugConcreteSubtype(newWidget);     
     ///確保類型一致                                  
      hasSameSuperclass = oldElementClass == newWidgetClass;                                                    
      return true;                                                                                              
    }());
    ///widget相等                                                                                                       
    if (hasSameSuperclass && child.widget == newWidget) { 
      ///只有一個child的`element`鬼店,它的`slot`為null                                                      
      if (child.slot != newSlot) 
      ///作用:修改給定的`child`在其`parent`中占據(jù)的slot。
     ///調(diào)用時機(jī):`MultiChildRenderObjectElement`
     ///和其他擁有多個渲染對象的`RenderObjectElement`的子類黔龟。
     ///當(dāng)它們的`child`從`element`的`child`(renderObject)數(shù)組
     ///的一個位置移動到其他位置時妇智。                                                                     
      updateSlotForChild(child, newSlot);                                                                     
      newChild = child;                                                                                         
    } else if (hasSameSuperclass && Widget.canUpdate(child.widget, newWidget)) {  
     ///詢問Widget.canUpdate                              
      if (child.slot != newSlot)   
        ///修改給定的`child`在其`parent`中占據(jù)的slot。                                                                             
        updateSlotForChild(child, newSlot);
      ///使用新的配置氏身,更新當(dāng)前child element  
      child.update(newWidget);                                                                                  
      assert(child.widget == newWidget);                                                                        
      assert(() {                                                                                               
        child.owner._debugElementWasRebuilt(child);                                                             
        return true;                                                                                            
      }());                                                                                                     
      newChild = child;                                                                                         
    } else { 
      /// 棄用當(dāng)前的child element                                                                                              
      deactivateChild(child);                                                                                   
      assert(child._parent == null);
     ///注入新的widget                                                                            
      newChild = inflateWidget(newWidget, newSlot);                                                             
    }                                                                                                           
  } else { 
     ///child == null   創(chuàng)建新的                                                                                            
    newChild = inflateWidget(newWidget, newSlot);                                                               
  }                                                                                                             
                                                                                                                
  assert(() {                                                                                                   
    if (child != null)                                                                                          
      _debugRemoveGlobalKeyReservation(child);                                                                  
    final Key key = newWidget?.key;                                                                             
    if (key is GlobalKey) {                                                                                     
      key._debugReserveFor(this, newChild);                                                                     
    }                                                                                                           
    return true;                                                                                                
  }());                                                                                                         
                                                                                                                
  return newChild;                                                                                              
}                                                                                                               
  • void update(covariant Widget newWidget)

描述:使用新widget修改用于配置此elementwidget巍棱。

調(diào)用時機(jī):當(dāng)parent element希望使用不同的widget更新此element時,并且新的widget必須保證和舊的widget擁有相同的runtimeType蛋欣。

  • Element inflateWidget(Widget newWidget, dynamic newSlot)

描述:為給定的widget創(chuàng)建一個element航徙,并且將其作為child添加到當(dāng)前element的給定slot中。

調(diào)用時機(jī):此方法通常由updateChild調(diào)用陷虎,但也可以由需要對創(chuàng)建的element進(jìn)行更細(xì)粒度控制的子類直接調(diào)用到踏。

注意: 如果給定的widgetglobal key,并且其對應(yīng)的element已經(jīng)存在泻红,這個函數(shù)將復(fù)用此element(可能從樹中的另一個位置移植它夭禽,或從inactive元素列表中重新激活它)而不是創(chuàng)建一個新的元素。此方法返回的element已經(jīng)被mounted并且處于active狀態(tài)谊路。

  Element inflateWidget(Widget newWidget, dynamic newSlot) {
    assert(newWidget != null);
    final Key key = newWidget.key;
   ///判斷是否是`GlobalKey`
    if (key is GlobalKey) {
      ///從處于`inactive`的列表中讹躯,重新獲取。
     ///1.final Element element = key._currentElement;
     ///2.element == null或!Widget.canUpdate(element.widget, newWidget)      
     ///reture null
     ///3. final Element parent = element._parent;
     ///4. parent.forgetChild(element);
     ///5. parent.deactivateChild(element);
     ///6.owner._inactiveElements.remove(element);
    ///7.return element 
      final Element newChild = _retakeInactiveElement(key, newWidget);
      if (newChild != null) {
        assert(newChild._parent == null);
        assert(() {
          _debugCheckForCycles(newChild);
          return true;
        }());
        ///重新激活獲得的`element`缠劝。
        ///1.修改`parent`
        ///2._updateDepth(int parentDepth)
        ///3.遞歸遍歷修改`element`中所有子元素的狀態(tài)未`active`.
       ///針對擁有`children`的`element`潮梯。
       ///4.調(diào)用attachRenderObject(dynamic newSlot)
      ///添加渲染對象到樹中。
        newChild._activateWithParent(this, newSlot);
       ///使用新的`widget`,更新此`element`,在新的`slot`上
        final Element updatedChild = updateChild(newChild, newWidget, newSlot);
        assert(newChild == updatedChild);
       ///返回updatedChild
        return updatedChild;
      }
    }
    ///如果不含globalKey
   ///創(chuàng)建新的元素
    final Element newChild = newWidget.createElement();
    assert(() {
      _debugCheckForCycles(newChild);
      return true;
    }());
   ///掛載:加入樹中惨恭。
    newChild.mount(this, newSlot);
    assert(newChild._debugLifecycleState == _ElementLifecycle.active);
    return newChild;
  }
  • void deactivateChild(Element child)

描述:inactive elements 列表中移除給定的element并且 從渲染樹中分離此element所對應(yīng)的render object秉馏。

調(diào)用時機(jī):一般在元素更新(刪除)其子元素時調(diào)用,但是在global key更換父節(jié)點時脱羡,新父會主動調(diào)用舊父的deactivateChild 萝究,并且在此之前會先調(diào)用舊父的forgetChild,對舊父進(jìn)行更新锉罐。

 void deactivateChild(Element child) {
    assert(child != null);
    assert(child._parent == this);
    ///去父
    child._parent = null;
    ///分離其渲染對象
    child.detachRenderObject();
    ///加入不活躍的數(shù)組中
    owner._inactiveElements.add(child); // this eventually calls child.deactivate()
    assert(() {
      if (debugPrintGlobalKeyedWidgetLifecycle) {
        if (child.widget.key is GlobalKey)
          debugPrint('Deactivated $child (keyed child of $this)');
      }
      return true;
    }());
  }
  • void detachRenderObject()

描述:從渲染樹中移除渲染對象帆竹。

調(diào)用時機(jī):一般在deactivateChild方法中被調(diào)用

注意:該函數(shù)的默認(rèn)實現(xiàn)只是對其child遞歸調(diào)用detachRenderObject,但是RenderObjectElement.detachRenderObject重寫了此方法后脓规,對其進(jìn)行了覆蓋(未調(diào)用super)栽连。

  void detachRenderObject() {
    visitChildren((Element child) {
      child.detachRenderObject();
    });
    _slot = null;
  }
  • void forgetChild(Element child)

描述:從元素的child列表中刪除給定的child,以準(zhǔn)備在元素樹中其他位置重用該child

調(diào)用時機(jī):一般在deactivateChild方法調(diào)用前秒紧。update會負(fù)責(zé)創(chuàng)建或者更新用于替換此child的新的child绢陌。
關(guān)于對此方法的詳細(xì)用法可以查看MultiChildRenderObjectElement類,此類中forgetChild會影響update時對于replaceWithNullIfForgotten方法的調(diào)用熔恢,也會影響visitChildren時對于子元素的遍歷脐湾。

  • void updateSlotForChild(Element child, dynamic newSlot)

描述:修改給定的child在父元素中占據(jù)的插槽 。
調(diào)用時機(jī):MultiChildRenderObjectElement和其他擁有多個渲染對象的RenderObjectElement的子類绩聘。當(dāng)它們的childelementchild(renderObject)數(shù)組的一個位置移動到其他位置時沥割。

 void updateSlotForChild(Element child, dynamic newSlot) {
    assert(_debugLifecycleState == _ElementLifecycle.active);
    assert(child != null);
    assert(child._parent == this);
    void visit(Element element) {
      element._updateSlot(newSlot);
      if (element is! RenderObjectElement)
        element.visitChildren(visit);
    }
    visit(child);
  }
  • void activate()

描述:將元素的生命周期由inactive變?yōu)?code>active。
調(diào)用時機(jī):當(dāng)之前停用的元素重新合并到樹中時凿菩,框架將調(diào)用此方法。元素第一次激活時(即帜讲,狀態(tài)從initial 開始時)衅谷,framework不會調(diào)用此方法,而是通過調(diào)用mount似将。

void activate() {
    ....
    final bool hadDependencies = (_dependencies != null && _dependencies.isNotEmpty) || _hadUnsatisfiedDependencies;
    _active = true;
    // We unregistered our dependencies in deactivate, but never cleared the list.
    // Since we're going to be reused, let's clear our list now.
    _dependencies?.clear();
    _hadUnsatisfiedDependencies = false;
    _updateInheritance();
    assert(() {
      _debugLifecycleState = _ElementLifecycle.active;
      return true;
    }());
    if (_dirty)
      ///將元素添加到`dirty element`列表中获黔,
      ///以便在`WidgetsBinding.drawFrame`調(diào)用`buildScope`
     ///時將對其進(jìn)行重建。
      owner.scheduleBuildFor(this);
    if (hadDependencies)
      didChangeDependencies();
  }
  • void attachRenderObject(dynamic newSlot)

描述:renderObject添加到渲染樹中slot指定的位置在验。
調(diào)用時機(jī):該函數(shù)的默認(rèn)實現(xiàn)只是對其child遞歸調(diào)用attachRenderObject玷氏,但是RenderObjectElement.attachRenderObject重寫了此方法后,對其進(jìn)行了覆蓋(未調(diào)用super)腋舌。

 void attachRenderObject(dynamic newSlot) {
    assert(_slot == null);
    visitChildren((Element child) {
      child.attachRenderObject(newSlot);
    });
    _slot = newSlot;
  }
  • void unmount()

描述: 生命周期由inactive 變?yōu)?defunct盏触。
調(diào)用時機(jī):當(dāng)framework確定處于inactive狀態(tài)的元素永遠(yuǎn)不會被激活時調(diào)用。在每個動畫結(jié)束時块饺,framework會對任何保持inactive狀態(tài)的元素調(diào)unmount赞辩,以防止inactive元素保持inactive的時間超過單個動畫幀。調(diào)用此函數(shù)后授艰,該元素將不再合并到樹中辨嗽。

  • void reassemble()

描述: 用于快速開發(fā)的一種debugging策略。
調(diào)用時機(jī): 在調(diào)試期間重新組裝(reassemble)應(yīng)用程序時調(diào)用淮腾,例如:hot reload期間糟需。此函數(shù)僅在開發(fā)期間被調(diào)用,并且調(diào)用了reassemble 谷朝,build將至少被調(diào)用一次洲押。

  • void didChangeDependencies()

調(diào)用時機(jī): 當(dāng)此元素的依賴項更改時調(diào)用。比如 此元素依賴的InheritedElement更新了新的InheritedWidget并且InheritedWidget.updateShouldNotify返回true的時候徘禁,framework會調(diào)用此方法诅诱,通知此元素做出相應(yīng)的改變。

  • void markNeedsBuild()

描述: 將元素標(biāo)記為dirty送朱,并將其添加到全局的widget列表中娘荡,以在下一幀中進(jìn)行重建干旁。
調(diào)用時機(jī): setStatereassemble炮沐、didChangeDependencies時争群。

  • void rebuild()

調(diào)用時機(jī):

  1. 當(dāng)BuildOwner.scheduleBuildFor已經(jīng)標(biāo)記此元素為dirty時,由BuildOwner調(diào)用大年;
  2. 當(dāng)元素通過mount首次被構(gòu)建時會調(diào)用换薄;
  3. 當(dāng)元素的widget通過update已經(jīng)被改變時,會調(diào)用此方法翔试。

Flutter中的Element(上篇)
iOS 解決 [NSURL fileURLWithPath:] 對 # 的編碼問題
Xcode 調(diào)整導(dǎo)航目錄字體大小b
Swift 5.1 (21) - 泛型
Swift 5.1 (20) - 協(xié)議
Swift 5.1 (19) - 擴(kuò)展
Swift 5.1 (18) - 嵌套類型
Swift 5.1 (17) - 類型轉(zhuǎn)換與模式匹配
淺談編譯過程
深入理解HTTPS

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末轻要,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子垦缅,更是在濱河造成了極大的恐慌冲泥,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,858評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件壁涎,死亡現(xiàn)場離奇詭異凡恍,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)怔球,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評論 3 395
  • 文/潘曉璐 我一進(jìn)店門嚼酝,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人竟坛,你說我怎么就攤上這事闽巩。” “怎么了流码?”我有些...
    開封第一講書人閱讀 165,282評論 0 356
  • 文/不壞的土叔 我叫張陵又官,是天一觀的道長。 經(jīng)常有香客問我漫试,道長六敬,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,842評論 1 295
  • 正文 為了忘掉前任驾荣,我火速辦了婚禮外构,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘播掷。我一直安慰自己审编,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,857評論 6 392
  • 文/花漫 我一把揭開白布歧匈。 她就那樣靜靜地躺著垒酬,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上勘究,一...
    開封第一講書人閱讀 51,679評論 1 305
  • 那天矮湘,我揣著相機(jī)與錄音,去河邊找鬼口糕。 笑死缅阳,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的景描。 我是一名探鬼主播十办,決...
    沈念sama閱讀 40,406評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼超棺!你這毒婦竟也來了向族?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,311評論 0 276
  • 序言:老撾萬榮一對情侶失蹤说搅,失蹤者是張志新(化名)和其女友劉穎炸枣,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體弄唧,經(jīng)...
    沈念sama閱讀 45,767評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年霍衫,在試婚紗的時候發(fā)現(xiàn)自己被綠了候引。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,090評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡敦跌,死狀恐怖澄干,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情柠傍,我是刑警寧澤麸俘,帶...
    沈念sama閱讀 35,785評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站惧笛,受9級特大地震影響从媚,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜患整,卻給世界環(huán)境...
    茶點故事閱讀 41,420評論 3 331
  • 文/蒙蒙 一拜效、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧各谚,春花似錦紧憾、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春般眉,著一層夾襖步出監(jiān)牢的瞬間了赵,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評論 1 271
  • 我被黑心中介騙來泰國打工煤篙, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留斟览,地道東北人。 一個月前我還...
    沈念sama閱讀 48,298評論 3 372
  • 正文 我出身青樓辑奈,卻偏偏與公主長得像苛茂,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子鸠窗,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,033評論 2 355