InheritedWidget使用说明

InheritedWidget

概述

在Flutter进行界面开发时,我们经常会遇到数据传递的问题。由于Flutter采用节点树的方式组织页面,以致于一个普通页面的节点层级会很深。此时,我们如果还是一层层传递数据,当需要修改数据时,就会比较麻烦。

此时,我们需要一种机制,能够让某一个节点下的所有子节点,访问该节点下的数据。

InheritedWidget就满足了我们这一需求。

使用方法

我们创建一个继承InheritedWidget的Widget。

class MyInheritedWidget extends InheritedWidget {
  final int data;

  MyInheritedWidget(@required this.data, Widget child) : super(child: child);

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


  @override
  bool updateShouldNotify(MyInheritedWidget oldWidget) {
    return oldWidget.data != data;
  }


}

可以看到,我们提供了三个方法。

构造方法

InheritedWidget本身是一个没有界面的Widget,或者说是一个代理Widget,我们需要传入我们实际的Widget。

所以,在构造方法中,我们需要传入两个参数,一个是我们希望共享的数据(在本例中数据是int型,实际业务中共享的通常是一个相对复杂的数据),另一个就是我们带界面的Widget。

数据获取方法

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

我们看一下inheritFromWidgetOfExactType方法的源码:

  /// Obtains the nearest widget of the given type, which must be the type of a
  /// concrete [InheritedWidget] subclass, and registers this build context with
  /// that widget such that when that widget changes (or a new widget of that
  /// type is introduced, or the widget goes away), this build context is
  /// rebuilt so that it can obtain new values from that widget.
  
  /// The [aspect] parameter is only used when [targetType] is an
  /// [InheritedWidget] subclasses that supports partial updates, like
  /// [InheritedModel]. It specifies what "aspect" of the inherited
  /// widget this context depends on.
  InheritedWidget inheritFromWidgetOfExactType(Type targetType, { Object aspect });
  

注释说得很清楚,inheritFromWidgetOfExactType是获取指定context下的,最近的指定类型的Widget,并且此Widget必须是InheritedWidget的子类。

值得注意的是,它是一个静态方法。所以,只要是相同BuildContext我们即可以在任意子节点上,通过这个静态方法,获取到我们的InheritedWidget,然后读取或修改它的共享数据。

更新通知

这个方法,决定了是否通知子节点中StatefulWidgetdidChangeDependencies方法是否调用。

  /// Called when a dependency of this [State] object changes.
  ///
  /// For example, if the previous call to [build] referenced an
  /// [InheritedWidget] that later changed, the framework would call this
  /// method to notify this object about the change.
  ///
  /// This method is also called immediately after [initState]. It is safe to
  /// call [BuildContext.inheritFromWidgetOfExactType] from this method.
  ///
  /// Subclasses rarely override this method because the framework always
  /// calls [build] after a dependency changes. Some subclasses do override
  /// this method because they need to do some expensive work (e.g., network
  /// fetches) when their dependencies change, and that work would be too
  /// expensive to do for every build.
  @protected
  @mustCallSuper
  void didChangeDependencies() { }

StatefulWidget的didChangeDependencies方法就是与InheritedWidget配合使用的。只有当InheritedWidget发生更新并且决定通知时,didChangeDependencies才会调用。

使用场景

通常,当我们需要在Widget树,由父节点向子节点传递数据时,会使用InheritedWidget。一旦某个节点的Widget继承了InheritedWidget,那么它的子节点,不论深度是多少,都可以获取到继承了InheritedWidget的Widget,并取得其中的数据成员。

class TextWidget extends StatefulWidget {
  @override
  State createState() {
    return TextWidgetState();
  }

}

class TextWidgetState extends State {
  @override
  Widget build(BuildContext context) {
    print("refresh text");
    return Text(
        "count : ${MyInheritedWidget.getData(context).data}"
    );
  }

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

}

所以,InheritedWidget适合做某个子树的数据管理Widget。当某个页面、模块或控件依赖一个或一组数据时。我们可以在其上层,创建一个继承了InheritedWidget的数据管理Widget。

通过这样的方式,至少我们不用对数据进行层层传递。

你可能感兴趣的:(InheritedWidget使用说明)