InheritedWidget內(nèi)部實(shí)現(xiàn)原理淺析

使用示例

閱讀原理之前請先體驗(yàn)InheritedWidget的使用demo:數(shù)據(jù)傳遞/狀態(tài)管理 一InheritedWidget使用示例

實(shí)現(xiàn)原理分析

InheritedWidget定義

先看一下InheritedWidget的定義:

abstract class InheritedWidget extends ProxyWidget {
  /// Abstract const constructor. This constructor enables subclasses to provide
  /// const constructors so that they can be used in const expressions.
  const InheritedWidget({ Key key, Widget child })
    : super(key: key, child: child);

  @override
  InheritedElement createElement() => InheritedElement(this);

  /// Whether the framework should notify widgets that inherit from this widget.
  ///
  /// When this widget is rebuilt, sometimes we need to rebuild the widgets that
  /// inherit from this widget but sometimes we do not. For example, if the data
  /// held by this widget is the same as the data held by `oldWidget`, then we
  /// do not need to rebuild the widgets that inherited the data held by
  /// `oldWidget`.
  ///
  /// The framework distinguishes these cases by calling this function with the
  /// widget that previously occupied this location in the tree as an argument.
  /// The given widget is guaranteed to have the same [runtimeType] as this
  /// object.
  @protected
  bool updateShouldNotify(covariant InheritedWidget oldWidget);
}

InheritedWidget是一個(gè)繼承自 ProxyWidget 的抽象類。除了實(shí)現(xiàn)了一個(gè) createElement 方法之外,還定義了一個(gè) updateShouldNotify() 接口。 updateShouldNotify就是使用InheritedWidget要實(shí)現(xiàn)的接口,決定InheritedWidget變化時(shí),要不要通知子widget。

再看下ProxyWidget是什么鬼:

abstract class ProxyWidget extends Widget {
  /// Creates a widget that has exactly one child widget.
  const ProxyWidget({ Key key, @required this.child }) : super(key: key);

  /// The widget below this widget in the tree.
  ///
  /// {@template flutter.widgets.child}
  /// This widget can only have one child. To lay out multiple children, let this
  /// widget's child be a widget such as [Row], [Column], or [Stack], which have a
  /// `children` property, and then provide the children to that widget.
  /// {@endtemplate}
  final Widget child;
}

ProxyWidget里面沒什么特別的東西

InheritedWidget 共享數(shù)據(jù)傳遞機(jī)制

既然在InheritedWidget定義上沒有收獲,我們就從InheritedWidget使用上入手,每個(gè)自定義InheritedWidget都會實(shí)現(xiàn)一個(gè) of 靜態(tài)方法,這個(gè)of靜態(tài)方法是提供給InheritedWidget的子widget來訪問自定義InheritedWidget的,先看下我們示例中of方法怎么實(shí)現(xiàn):

  static ShareDataInheritedWidget of(BuildContext context) {
    return context.dependOnInheritedWidgetOfExactType();
  }

of方法直接返回context的dependOnInheritedWidgetOfExactType,看下dependOnInheritedWidgetOfExactType里面是什么:
dependOnInheritedWidgetOfExactType在源碼中兩處,一處是BuildContext,一處是Element:


image.png

BuildContext中dependOnInheritedWidgetOfExactType其實(shí)只是定義:

 T dependOnInheritedWidgetOfExactType<T extends InheritedWidget>({ Object aspect });

真正實(shí)現(xiàn)dependOnInheritedWidgetOfExactType是在Element (其實(shí)BuildContext就是Element的引用,這里就不作分析了) :

@override
  T dependOnInheritedWidgetOfExactType<T extends InheritedWidget>({Object aspect}) {
    assert(_debugCheckStateIsActiveForAncestorLookup());
    final InheritedElement ancestor = _inheritedWidgets == null ? null : _inheritedWidgets[T];
    if (ancestor != null) {
      assert(ancestor is InheritedElement);
      return dependOnInheritedElement(ancestor, aspect: aspect);
    }
    _hadUnsatisfiedDependencies = true;
    return null;
  }

 @override
  InheritedWidget dependOnInheritedElement(InheritedElement ancestor, { Object aspect }) {
    assert(ancestor != null);
    _dependencies ??= HashSet<InheritedElement>();
    _dependencies.add(ancestor);
    ancestor.updateDependencies(this, aspect);
    return ancestor.widget;
  }

可以看到,子widget通過_inheritedWidgets和我們自定義的InheritedWidget拿到對應(yīng)的InheritedElement,再從InheritedElement中拿到我們自定義的InheritedWidget;

_inheritedWidgets定義如下:

Map<Type, InheritedElement> _inheritedWidgets;

_inheritedWidgets是一個(gè)Map類型,以Type為key,定義在Element中,也就是每個(gè)Elemnet都有一個(gè)_inheritedWidgets;

這里插一段小曲:
Type定義如下:

/**
 * Runtime representation of a type.
 */
abstract class Type {}

可以在Object中找到它:

class Object {
...
  /**
   * A representation of the runtime type of the object.
   */
  external Type get runtimeType;
...
}

Object本身應(yīng)該就實(shí)現(xiàn)Type,類繼承自O(shè)bject,所以類也是Object,可以直接傳給上面Map類型為Type的 key,也就是傳給Map Type key其實(shí)傳的是類的runtimeType,經(jīng)驗(yàn)證,多次以相同類名作為key保存value到Map,最后以這個(gè)類名為key取出的value為最后存儲的value,類名作為key跟定義一個(gè)字符串作為key作用類似;

回到正題,我們看下_inheritedWidgets數(shù)據(jù)怎么來,因?yàn)開inheritedWidgets在Element中定義,所以在Element中先找:

void _updateInheritance() {
    assert(_active);
    _inheritedWidgets = _parent?._inheritedWidgets;
  }

發(fā)現(xiàn)_inheritedWidgets在_updateInheritance方法里初始化,_updateInheritance是在widget mounted或activate調(diào)用;知道_inheritedWidgets的數(shù)據(jù)來自父widget,那父widget的_inheritedWidgets從哪都是在來呢?
在InheritedWidget的定義中,發(fā)現(xiàn)InheritedWidget有自己的Element->InheritedElement

abstract class InheritedWidget extends ProxyWidget {
...
  @override
  InheritedElement createElement() => InheritedElement(this);
...

再看下InheritedElement源碼:

/// An [Element] that uses an [InheritedWidget] as its configuration.
class InheritedElement extends ProxyElement {
  /// Creates an element that uses the given widget as its configuration.
  InheritedElement(InheritedWidget widget) : super(widget);

  @override
  InheritedWidget get widget => super.widget;

  final Map<Element, Object> _dependents = HashMap<Element, Object>();

  @override
  void _updateInheritance() {
    assert(_active);
    final Map<Type, InheritedElement> incomingWidgets = _parent?._inheritedWidgets;
    if (incomingWidgets != null)
      _inheritedWidgets = HashMap<Type, InheritedElement>.from(incomingWidgets);
    else
      _inheritedWidgets = HashMap<Type, InheritedElement>();
    _inheritedWidgets[widget.runtimeType] = this;
  }

可以看到InheritedElement重寫了_updateInheritance方法,當(dāng)父widget的_inheritedWidgets為空時(shí),實(shí)例化_inheritedWidgets;當(dāng)父widget不為空時(shí),也實(shí)例化_inheritedWidgets,并把父widget的_inheritedWidgets加進(jìn)來,這里有個(gè)細(xì)節(jié),為什么不直接用父widget的_inheritedWidgets呢,這個(gè)后面會分析;最后關(guān)鍵的一步,就是將當(dāng)前自定義InheritedWidget對應(yīng)的Element保存到以自定義InheritedWidget類別作為key的_inheritedWidgets里,這樣_inheritedWidgets就保存了我們自定義InheritedWidget的數(shù)據(jù)了;InheritedWidget的_inheritedWidgets傳給子widget,子widget就可以通過_inheritedWidgets拿到我們自定義InheritedWidget的數(shù)據(jù)了;

到這里我們可以知道:(1)、每個(gè)widget對應(yīng)的Element都有一個(gè)_inheritedWidgets,widget樹自上而下,一級一級將自己的_inheritedWidgets傳給下一級,并且當(dāng)前級別為InheritedWidget時(shí),就會將當(dāng)前自定義的InheritedWidget保存在_inheritedWidgets;
(2)、通過_inheritedWidgets一級一級的向下傳遞,這樣每個(gè)子widget的_inheritedWidgets就擁有上級父widget的所有類別的InheritedWidget,就可以通過自身的_inheritedWidgets訪問到對應(yīng)的InheritedWidget;

InheritedWidget 數(shù)據(jù)更新通知機(jī)制

我們自定義的變化InheritedWidget是怎么知道到子widget的didChangeDependencies呢?
子widget跟父InheritedWidget使用關(guān)聯(lián)只有of靜態(tài)方法,所以我們再從of方法里仔細(xì)分析:

 @override
  InheritedWidget dependOnInheritedElement(InheritedElement ancestor, { Object aspect }) {
    assert(ancestor != null);
    _dependencies ??= HashSet<InheritedElement>();
    _dependencies.add(ancestor);
    ancestor.updateDependencies(this, aspect);
    return ancestor.widget;
  }

發(fā)現(xiàn)在子widget的dependOnInheritedElement方法里,InheritedWidget的element->InheritedElement,也就是ancestor,調(diào)用了updateDependencies方法將子widget的element傳遞過去;
來看下InheritedElement的updateDependencies的方法:

class InheritedElement extends ProxyElement {
...
final Map<Element, Object> _dependents = HashMap<Element, Object>();
@protected
  void updateDependencies(Element dependent, Object aspect) {
    setDependencies(dependent, null);
  }
  @protected
  void setDependencies(Element dependent, Object value) {
    _dependents[dependent] = value;
  }

InheritedElement的updateDependencies方法將子widget對應(yīng)的element作為key,保存在InheritedElement的_dependents中,這樣我們自定義的InheritedWidget就可以通過自身的InheritedElement,找到關(guān)聯(lián)的子widget的element了;
我們定義的的InheritedWidget有了需要通知的子widget,再看看InheritedWidget如何在變化時(shí)通知子widget的;
在自定義InheritedWidget時(shí),我們需要實(shí)現(xiàn)接口方法:updateShouldNotify,先找下updateShouldNotify在哪里調(diào)用到:


來自ProxyElement:
  @override
  void update(ProxyWidget newWidget) {
    ...
    updated(oldWidget);
    ...
  }

來自InheritedElement:
  @override
  void updated(InheritedWidget oldWidget) {
    if (widget.updateShouldNotify(oldWidget))
      super.updated(oldWidget);
  }

來自ProxyElement:
  @protected
  void updated(covariant ProxyWidget oldWidget) {
    notifyClients(oldWidget);
  } 

來自InheritedElement:
  @override
  void notifyClients(InheritedWidget oldWidget) { 
    for (Element dependent in _dependents.keys) {
      notifyDependent(oldWidget, dependent);
    }
  }
}

來自InheritedElement:
  @protected
  void notifyDependent(covariant InheritedWidget oldWidget, Element dependent) {
    dependent.didChangeDependencies();
  }

這里比較繞,需要仔細(xì)理解InheritedElement 和 ProxyElement關(guān)系(InheritedElement extends ProxyElement),update和updated方法區(qū)別;
由此可知:

  • 在InheritedWidget更新變化時(shí),通過updateShouldNotify返回值決定要不要通知相關(guān)的子widget;
  • 如果updateShouldNotify返回false,則相關(guān)子widget 的didChangeDependencies不會調(diào)用到;
  • 如果返回true,則會遍歷_dependents,找到子widget相關(guān)的element,并調(diào)用element的didChangeDependencies方法;

自定義of靜態(tài)方法內(nèi)部實(shí)現(xiàn)用getElementForInheritedWidgetOfExactType為何子widget的didChangeDependencies就不會接收到InheritedWidget更新通知?

我們知道,自定義of靜態(tài)方法內(nèi)部實(shí)現(xiàn)用dependOnInheritedWidgetOfExactType子widget的didChangeDependencies就會接收到InheritedWidget更新,所以先看下兩個(gè)方法內(nèi)部實(shí)現(xiàn)的差異:
dependOnInheritedWidgetOfExactType:

  @override
  T dependOnInheritedWidgetOfExactType<T extends InheritedWidget>({Object aspect}) {
    assert(_debugCheckStateIsActiveForAncestorLookup());
    final InheritedElement ancestor = _inheritedWidgets == null ? null : _inheritedWidgets[T];
    if (ancestor != null) {
      assert(ancestor is InheritedElement);
      return dependOnInheritedElement(ancestor, aspect: aspect);
    }
    _hadUnsatisfiedDependencies = true;
    return null;
  }

getElementForInheritedWidgetOfExactType:

  @override
  InheritedElement getElementForInheritedWidgetOfExactType<T extends InheritedWidget>() {
    assert(_debugCheckStateIsActiveForAncestorLookup());
    final InheritedElement ancestor = _inheritedWidgets == null ? null : _inheritedWidgets[T];
    return ancestor;
  }

從兩個(gè)方法內(nèi)部代碼實(shí)現(xiàn)對比可知:

  • dependOnInheritedWidgetOfExactType內(nèi)部實(shí)現(xiàn)比getElementForInheritedWidgetOfExactType內(nèi)部實(shí)現(xiàn)多調(diào)用了dependOnInheritedElement方法;
  • 從上面分析可知,dependOnInheritedElement注冊子widget和InheritedWidget依賴關(guān)系,InheritedWidget變化時(shí)通過這層依賴關(guān)系通知子widget的didChangeDependencies;
  • 所以dependOnInheritedWidgetOfExactType() 和 getElementForInheritedWidgetOfExactType()的區(qū)別就是前者子widget和InheritedWidget會注冊依賴關(guān)系,而后者不會,所以用dependOnInheritedWidgetOfExactType實(shí)現(xiàn)的of方法對應(yīng)的InheritedWidget變化時(shí)會通知子widget的didChangeDependencies方法,而用getElementForInheritedWidgetOfExactType則InheritedWidget變化不會通知子widget;

Widget樹上有多個(gè)相同類別的InheritedWidget,為何子widget只會找到最近的父InheritedWidget?

從上面分析我們知道,每個(gè)子widget的_inheritedWidgets數(shù)據(jù)初始化是在_updateInheritance 方法進(jìn)行的,其實(shí)InheritedElement重寫了_updateInheritance方法,如下:,


image.png

普通widget的Element的_updateInheritance方法:

 void _updateInheritance() {
    assert(_active);
    _inheritedWidgets = _parent?._inheritedWidgets;
  }

InheritedWidget的InheritedElement的_updateInheritance方法:

  @override
  void _updateInheritance() {
    assert(_active);
    final Map<Type, InheritedElement> incomingWidgets = _parent?._inheritedWidgets;
    if (incomingWidgets != null)
      _inheritedWidgets = HashMap<Type, InheritedElement>.from(incomingWidgets);
    else
      _inheritedWidgets = HashMap<Type, InheritedElement>();
    _inheritedWidgets[widget.runtimeType] = this;
  }

對比上面代碼,我們可以看到,普通widget只是把父widget的_inheritedWidgets直接賦值到自身的_inheritedWidgets;而InheritedWidget是先創(chuàng)建新的_inheritedWidgets,再把父widget的_inheritedWidgets的數(shù)據(jù)加到自己創(chuàng)建的_inheritedWidgets中,同時(shí)以自己的類別名作為key,將自己的element實(shí)例保存到自己創(chuàng)建的_inheritedWidgets;
為什么InheritedWidget要自己創(chuàng)建_inheritedWidgets呢?
這樣做的好處是:
(1)、因?yàn)槿绻苯佑酶傅腳inheritedWidgets,當(dāng)將自身的element加到_inheritedWidgets時(shí),如果父的_inheritedWidgets數(shù)據(jù)中有和自身相同類別的InheritedWidget數(shù)據(jù),就會把父_inheritedWidgets和自身相同類別的InheritedWidget的數(shù)據(jù)替換掉,也就是一級傳一傳的_inheritedWidgets,最終只有一種類別的InheritedWidget,這樣傳遞數(shù)據(jù)就會混亂;
(2)、這也是為什么子widget只會找到最近的父InheritedWidget的原因:因?yàn)樵谶@層的InheritedWidget創(chuàng)建新的_inheritedWidgets,并以自身的類別作為key,將自己的element實(shí)例保存到自己創(chuàng)建的_inheritedWidgets,如果_inheritedWidgets中有和自身相同類別的InheritedWidget數(shù)據(jù),替換的也只是自身創(chuàng)建的_inheritedWidgets中數(shù)據(jù),不會替換掉父_inheritedWidgets中和自己相同類別保存的數(shù)據(jù);然后再將自身的_inheritedWidgets傳給子widget,這樣子widget通過of靜態(tài)方法拿到的InheritedWidget就是離它最近的父InheritedWidget的數(shù)據(jù)了。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容