FlutterStateManagement-継承されたウィジェットデータ共有の主成分分析

AliExpressWidgetとは

これは、AttachedWidgetのドキュメントコメントで説明されています。

ツリーの下に情報を効率的に伝播するウィジェットの基本クラス。

レンダーツリーのサブツリーに情報を効率的に渡すための基本クラス。

アプリのエントリから:

void main() {
  runApp(MyApp());
  // runApp(CustomInheritedWidget());
}

レンダリングツリーの構築を開始します。MyApp()はツリーのルートノードと見なすことができます。ルートノードのウィジェットがAttachedWidgetに設定されている場合、そのサブツリー内のすべてのサブツリーサブノードをAttachedWidget共有データで取得できます。 。AttachedWidgetのデータが変更されると、データに依存するすべての子ウィジェットが再構築されます。

用途は何ですか

「共有、共変動」の特徴を中心に

  • 統一されたテーマ設定では、テーマを変更すると、すべてのサブページが即座に変更されます。
  • ユーザー情報や権限などの公開データの共有など、プロジェクトの基本データ。
  • 状態管理
  • .....。

コア使用プロセス

  • カスタムCustomAliExpressWidgetは、AttachedWidgetを継承し、メンバー変数として共有されるデータを設定します。updateShouldNotifyメソッドをオーバーライドし、この共有データに依存するウィジェットがどのような状況で再構築されるかをこのメソッドで指定します。
  • このデータに依存する必要のあるウィジェットで共有データを取得します。CustomAttachedWidget.of(context).data.toString()。データはカスタム共有データであり、他のデータでもかまいません。共有データが変更されたときにトリガーされるdidChangeDependenciesメソッドをオーバーライドします。
  • 依存関係をCustomAliExpressWidgetの子ウィジェットにします

ソースコード分析

  • AttachedWidgetの場合、パラメーターは子であり、依存関係は子またはこの子の子孫に設定されます。Widgetのコア機能は要素のデータを構成することであり、ここでのAliExpressElementは実際に機能するクラスです。updateShouldNotifyメソッドは、オーバーライドされたAliExpressElementのメソッドでもあり、これらの依存関係に更新を通知するために使用されます。
abstract class InheritedWidget extends ProxyWidget {
  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.
  @protected
  bool updateShouldNotify(covariant InheritedWidget oldWidget);
}
  • AliExpressElementと入力します
class InheritedElement extends ProxyElement {
  ...
  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;
  }

  @override
  void debugDeactivated() {
    assert(() {
      assert(_dependents.isEmpty);
      return true;
    }());
    super.debugDeactivated();
  }

  /// Returns the dependencies value recorded for [dependent]
  
  @protected
  Object getDependencies(Element dependent) {
    return _dependents[dependent];
  }

  /// Sets the value returned by [getDependencies] value for [dependent].
  
  @protected
  void setDependencies(Element dependent, Object value) {
    _dependents[dependent] = value;
  }

  /// Called by [dependOnInheritedWidgetOfExactType] when a new [dependent] is added.
  
  @protected
  void updateDependencies(Element dependent, Object aspect) {
    setDependencies(dependent, null);
  }

  /// Called by [notifyClients] for each dependent.
  
  @protected
  void notifyDependent(covariant InheritedWidget oldWidget, Element dependent) {
    dependent.didChangeDependencies();
  }

  /// Calls [Element.didChangeDependencies] of all dependent elements, if
  /// [InheritedWidget.updateShouldNotify] returns true.
  
  @override
  void updated(InheritedWidget oldWidget) {
    if (widget.updateShouldNotify(oldWidget))
      super.updated(oldWidget);
  }

  /// Notifies all dependent elements that this inherited widget has changed, by
  /// calling [Element.didChangeDependencies].
 
  @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);
    }
  }
}

_dependents:すべての扶養家族への参照を格納するマップを維持します。

** _ inheritedWidgets:**これは、祖先クラスElementの属性です。これは、AttachedElementの場合にのみ機能します。これにより、祖先ノードに表示されるすべてのAttachedWidgetsとAttachedElementsの間の対応が保存されます。_inheritedWidgets[widget.runtimeType] = this、here widget.runtimeTypeは、Objectのプロパティであり、このクラスを一意に表します。これは、AttachedElementです。

** _ updateInheritance():**このメソッドはElementのメソッドでもあり、Elementのmou​​nt()で呼び出されて_inheritedWidgetsを更新します。このメソッドには、Elementにデフォルトの実装があり、次のようになっていることに注意してください。

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

つまり、親ノードに_inheritedWidgetsがあるかどうかを判断し、ある場合は、それを現在の要素の_inheritedWidgetsに割り当てます。このようにして、各レイヤーの要素は前のレイヤーの_inheritedWidgetsを保持します。これが理由です。 AliExpressWidgetは、データを渡す理由をずっと下に行くことができます。そして、AliExpressElementはこのメソッドをオーバーライドします。主にこのコード:_inheritedWidgets [widget.runtimeType] = this、マッピングを更新します。現在のAttachedWidgetページを_inheritedWidgetsに追加します。

** getDependencies():**すべての依存関係を取得します** setDependencies():**新しい依存関係を追加します。一般に、AttachedWidgetをカスタマイズする場合、の静的メソッドは、次のようなカスタムDependedWidgetを取得するために定義されます。

static MyInheritedWidget of(BuildContext context) {
    return context.inheritFromWidgetOfExactType(MyInheritedWidget);
  }

context.inheritFromWidgetOfExactTypeメソッド内に移動します。

InheritedWidget inheritFromWidgetOfExactType(Type targetType, { Object aspect }) {
    assert(_debugCheckStateIsActiveForAncestorLookup());
    final InheritedElement ancestor = _inheritedWidgets == null ? null : _inheritedWidgets[targetType];
    if (ancestor != null) {
      assert(ancestor is InheritedElement);
      return inheritFromElement(ancestor, aspect: aspect);
    }
    _hadUnsatisfiedDependencies = true;
    return null;
  }

継承FromElement()に移ります:

@override
  InheritedWidget inheritFromElement(InheritedElement ancestor, { Object aspect }) {
    return dependOnInheritedElement(ancestor, aspect: aspect);
  }

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

最終的な修正は次のとおりです。setDependencies(dependent、null);依存関係がカスタムのAttachedWidgetを取得するために使用すると、依存関係のセットにそれ自体が追加されることがわかります。** updateDependencies():**指定された依存のnotifyDependent()を更新します:、依存のdidChangeDependenciesメソッドを呼び出して、依存データが変更されたことを通知します。このメソッドは、notifyClients()メソッドによってバッチで呼び出されます。notifyClients():、データが変更されたことをバッチで扶養家族に通知します。このメソッドはProxyElemntで定義され、AttachedElementでオーバーライドされます。カスタムAliExpressWidgetのデータが変更されると、オーバーライドされたメソッドupdateShouldNotifyを使用して、依存関係に更新を通知する必要があるかどうかを定義します。updateShouldNotifyは次のとおりです。

@override
  bool updateShouldNotify(MyInheritedWidget oldWidget) {
    // 新旧数据不一致时,返回true,通知依赖本widget的子widget,此时子widget中的didChangeDependencies方法会被调用
    return oldWidget.data != data;
  }

新旧のデータが異なる場合は、通知する必要があります。ここでのリターンは、実際の状況に応じて定義できます。updateShouldnotifyメソッドはいつ呼び出されますか?AliExpressElementの更新されたメソッドの内部:

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

updated()メソッドはProxyElementで定義され、super.notifyClients()メソッドが呼び出されます。これは、次のようにProxyElementに実装されます。

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

notifyClients()は、ProxyELementの空の実装であり、AttachedElementで書き直されています。これにより、プロセス全体が結び付けられます。

AttachedWidgetの内部プロセス

  • AliExpressWidgetをカスタマイズし、共有データを設定し、updateShouldNotifyメソッドを書き直して、静的メソッドを提供します。
  • 扶養家族は、AttachedWidgetの子孫になります。扶養家族がofメソッドを呼び出して共有データを取得すると、扶養家族は内部的に扶養家族リスト_dependentsに追加されます。
  • 要素ツリーの構築中に、_inheritedWidgetsは、マウント中に_updateInheritanceメソッドを介してレイヤーごとにダウンロードされます。ダウンロードプロセス中に、非DependentWidgetタイプのウィジェットは、親の_inheritedWidgetsを直接割り当てますが、InheritedWidgetタイプのウィジェットは直接割り当てられます。親の_inheritedWidgetsを自分自身に直接割り当てます。親の_inheritedWidgetsはそれ自体に割り当てられ、それに追加されます。
  • 共有データが変更されると、updatedメソッドはupdateShouldNotifyがtrueかどうかを判断します。trueの場合、notifyClientsメソッドを呼び出します。notifyClientsは内部でdependent.didChangeDependencies();を呼び出し、最後に依存関係のdidChangeDependenciesメソッドを呼び出します。
  • 継承されたウィジェット(カスタム素材の継承ウィジェット)の共有データは読み取り専用です。データの変更を更新する場合でも、StatefulWidgetのsetState(カスタムStatefulWidget)に依存する必要があります。

  • クロスページステータスはサポートされていません。
    ヘリテージウィジェットを使用してページAにデータを保存し、ページBまたはページCにジャンプすると、コンテキストを使用してページAの継承要素を取得できないことがわかります(ページAはページB、ページにジャンプします) Bはページの子ノードではありません)

おすすめ

転載: blog.csdn.net/jdsjlzx/article/details/123304429