Comprendre Flutter InheritedWidget via le code source

 Le cœur d'InheritedWidget est d'enregistrer la valeur et les widgets qui utilisent cette valeur. En comparant les modifications de la valeur, il détermine s'il doit avertir les widgets qui utilisent cette valeur de se mettre à jour.

1 updateShouldNotify et notifyClients

InheritedWidget utilise la fonction updateShouldNotify pour contrôler si les sous-composants qui en dépendent seront reconstruits lorsque InheritedWidget change : si updateShouldNotify renvoie true, la construction du sous-composant sera appelée lorsque InheritedWidget change, et vice versa.

méthode mise à jour dans InheritedElement :

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

InheritedElement hérite de ProxyElement, et l'implémentation mise à jour de ProxyElement est :

@protected
  void updated(covariant ProxyWidget oldWidget) {
    notifyClients(oldWidget);
  }

L'implémentation notifyClients de InheritedElement parcourt les widgets qui s'appuient sur lui-même dans _dependents, puis appelle ses didChangeDependencies pour la notification des modifications :

  @override
  void notifyClients(InheritedWidget oldWidget) {
    assert(_debugCheckOwnerBuildTargetExists('notifyClients'));
    for (final Element dependent in _dependents.keys) {
      assert(() {
        // check that it really is our descendant
        Element? ancestor = dependent._parent;
        while (ancestor != this && ancestor != null) {
          ancestor = ancestor._parent;
        }
        return ancestor == this;
      }());
      // check that it really depends on us
      assert(dependent._dependencies!.contains(this));
      notifyDependent(oldWidget, dependent);
    }
  }

@protected
  void notifyDependent(covariant InheritedWidget oldWidget, Element dependent) {
    dependent.didChangeDependencies();
  }
Element的didChangeDependencies源码如下:

void didChangeDependencies() {
  assert(_active); // otherwise markNeedsBuild is a no-op
  assert(_debugCheckOwnerBuildTargetExists('didChangeDependencies'));
  markNeedsBuild();
}

On peut voir que lorsque InheritedWidget change, si updateShouldNotify est vrai, la fonction didChangeDependencies du sous-composant sera appelée via notifyClients, appelant ainsi markNeedsBuild et ajoutant cet élément à la liste _dirtyElements. Comme nous le savons tous, _dirtyElements stocke les éléments qui doivent être reconstruits et qui le seront dans la trame suivante, de sorte que le sous-composant sera reconstruit dans la trame suivante.

2 Transfert du widget hérité

InheritedWidget peut être utilisé pour permettre aux utilisateurs d'obtenir rapidement des sous-composants. Comment y parvenir ?

En fait, Flutter Framework transmet également InheritedWidget couche par couche, mais comme la couche Framework le gère elle-même, ce processus est transparent pour nous. Voyons maintenant le processus de transfert d'InheritedWidget.

Dans Element, il y a une carte : _inheritedWidgets. Les InheritedElements dans tous les nœuds supérieurs sont enregistrés. Son code source est le suivant :

Map<Type, InheritedElement> _inheritedWidgets;

Parmi eux, Type in key est une sous-classe de InheritedWidget et la valeur est InheritedElement. Pourquoi la valeur ici enregistre-t-elle InheritedElement au lieu de InheritedWidget ? D'après l'article précédent, nous pouvons savoir que la référence au Widget correspondant est stockée dans Element, donc le InheritedWidget correspondant peut être obtenu via InheritedElement. De plus, le Widget sera reconstruit lorsque le Widget supérieur sera reconstruit, il est donc plus approprié de sauvegarder InheritedElement.

Dans un élément normal, _inheritedWidgets copiera directement la valeur de _inheritedWidgets dans son composant parent. Le code source est le suivant :

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

Dans InheritedElement, _inheritedWidgets copiera d'abord la valeur de _inheritedWidgets dans son composant parent, puis s'ajoutera à la liste. Le code source est le suivant :

@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;
}

On peut voir à partir de là que InheritedElement est transmis couche par couche de cette manière. Le processus d'attribution de _inheritedWidgets est le suivant :

Comme le montre l'organigramme, _inheritedWidgets a été attribué lorsque l'élément est ajouté à l'arborescence des éléments, afin qu'il soit accessible dans la fonction de construction du sous-composant.

3 Dépendances d'acquisition et d'enregistrement d'InheritedWidget

Nous savons déjà qu'InheritedElement sera transmis aux composants subordonnés, alors comment l'obtenir ? Flutter fournit une fonction dependOnInheritedWidgetOfExactType qui obtient spécifiquement un type InheritedWidget. Son code source est le suivant :

@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) as T;
  }
  _hadUnsatisfiedDependencies = true;
  return null;
}

Comme le montre la troisième ligne, cette fonction trouvera l'InheritedElement correspondant à partir de _inheritedWidgets et renverra son InheritedWidget.

En plus de dependOnInheritedWidgetOfExactType, Flutter fournit également une autre fonction qui obtient spécifiquement un type InheritedWidget : getElementForInheritedWidgetOfExactType. Son code source est le suivant :

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

En le comparant avec le code source de dependOnInheritedWidgetOfExactType, vous pouvez voir que dependOnInheritedWidgetOfExactType a plus d'appels à la fonction dependOnInheritedElement, qui est utilisée pour créer des dépendances entre InheritedWidget et les composants qui appellent dependOnInheritedWidgetOfExactType. Il comporte deux étapes :

  • Ajoutez le InheritedElement dépendant à la liste _dependencies de cet élément, qui stocke tous les InheritedElements dont dépend cet élément.
  • Ajoutez cet élément à la carte _dependents de l'InheritedElement dépendant. Cette liste stocke tous les éléments qui dépendent de l'InheritedElement.

Si dependOnInheritedWidgetOfExactType est utilisé, lorsque le InheritedWidget dépendant est mis à jour, le sous-composant dépendant sera reconstruit ; et lorsque getElementForInheritedWidgetOfExactType est utilisé, puisque la dépendance correspondante ne sera pas établie, lorsque le InheritedWidget est mis à jour, le sous-composant dépendant ne sera pas construit. reconstruit.

4 Appelez activement dependOnInheritedWidgetOfExactType

 Le sous-composant doit obtenir le InheritedWidget en appelant dependOnInheritedWidgetOfExactType et s'ajouter à la dépendance du InheritedWidget. Pour plus de commodité, la méthode of est généralement implémentée dans la sous-classe InheritedWidget :

class ShareDataWidget extends InheritedWidget {
  ShareDataWidget({
    @required this.data,
    Widget child
  }) :super(child: child);

  final int data; //需要在子树中共享的数据,保存点击次数

  //定义一个便捷方法,方便子树中的widget获取共享数据
  static ShareDataWidget of(BuildContext context) {
    return context.dependOnInheritedWidgetOfExactType<ShareDataWidget>();
  }

  //该回调决定当data发生变化时,是否通知子树中依赖data的Widget
  @override
  bool updateShouldNotify(ShareDataWidget old) {
    //如果返回true,则子树中依赖(build函数中有调用)本widget
    //的子widget的`state.didChangeDependencies`会被调用
    return old.data != data;
  }
}

class _TestWidget extends StatefulWidget {
  @override
  __TestWidgetState createState() => new __TestWidgetState();
}

class __TestWidgetState extends State<_TestWidget> {
  @override
  Widget build(BuildContext context) {
    print("__TestWidgetState build");
    //使用InheritedWidget中的共享数据
    return Text(ShareDataWidget
        .of(context)
        .data
        .toString());
    // return Text("tex");
  }

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    //父或祖先widget中的InheritedWidget改变(updateShouldNotify返回true)时会被调用。
    //如果build中没有依赖InheritedWidget,则此回调不会被调用。
    print("Dependencies change");
  }
}

référence:

Analyse du framework Flutter-InheritedWidget - Zhihu

Je suppose que tu aimes

Origine blog.csdn.net/Mamong/article/details/132569959
conseillé
Classement