美文网首页
[Flutter开发]06.使用InheritedWidget实

[Flutter开发]06.使用InheritedWidget实

作者: 沈枫_SerenF | 来源:发表于2020-03-26 21:54 被阅读0次

    接上一篇文章,全局管理状态的provider包可以简单通过InheritedWidget来实现,不过在此之前,需要先认识一下InheritedWidget。

    一. InheritedWidget

    InheritedWidget有个很重要的特性,它提供了一种方式,使得数据可以在widget树中从上到下传递、共享,比如在应用的根widget中通过InheritedWidget共享了一个数据,那么我们便可以在任意子widget中来获取该共享的数据!

    在之前介绍State的生命周期时,State对象有一个didChangeDependencies回调,它会在“依赖”发生变化时被Flutter Framework调用。而这个“依赖”指的就是子widget是否使用了父widget中InheritedWidget的数据!如果使用了,则代表子widget依赖有依赖InheritedWidget;如果没有使用则代表没有依赖。这种机制可以使子组件在所依赖的InheritedWidget变化时来更新自身!

    接下来,举个例子来说明一下。

    1. 先创建个继承InheritedWidget的类DataWidget:
    class DataWidget extends InheritedWidget {
      DataWidget({
        @required this.data,
        Widget child
      }) :super(child: child);
    
      final int data; 
    
      static DataWidget of(BuildContext context) {
        return context.inheritFromWidgetOfExactType(DataWidget);
      }
    
      //该回调决定当data发生变化时,是否通知子树中依赖data的Widget  
      @override
      bool updateShouldNotify(DataWidget old) {
        //true: 子树中依赖本widget,从而子widget的`state.didChangeDependencies`会被调用
        return old.data != data;
      }
    }
    
    1. 然后实现一个组件_TestWidget,在其build方法中使用DataWidget中的数据。
    class _TestWidget extends StatefulWidget {
      @override
      _TestWidgetState createState() => new _TestWidgetState();
    }
    
    class _TestWidgetState extends State<_TestWidget> {
      @override
      Widget build(BuildContext context) {
        //使用InheritedWidget中的共享数据
        return Text(DataWidget
            .of(context)
            .data
            .toString());
      }
    
      @override
      void didChangeDependencies() {
        super.didChangeDependencies();
        //父widget中的InheritedWidget改变(updateShouldNotify返回true)时会被调用。
        //如果build中没有依赖InheritedWidget,则此回调不会被调用。
        print("Dependencies change");
      }
    }
    
    1. 再在另一个组件中使用DataWidget,并使_TestWidget成为DataWidget的子组件。
    class TestRoute extends StatefulWidget {
      @override
      _TestRouteState createState() => new _TestRouteState();
    }
    
    class _TestRouteState extends State<TestRoute> {
      int count = 0;
    
      @override
      Widget build(BuildContext context) {
        return  Center(
          child: DataWidget( 
            data: count,
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                Padding(
                  padding: const EdgeInsets.only(bottom: 20.0),
                  child: _TestWidget(),//子widget中依赖DataWidget
                ),
                RaisedButton(
                  child: Text("Increment"),
                  //每次点击,count自增,然后重新build, DataWidget的data将被更新  
                  onPressed: () => setState(() => ++count),
                )
              ],
            ),
          ),
        );
      }
    }
    

    上面例子就产生了依赖,子组件_TestWidget 的didChangeDependencies()会被调用。如果_TestWidget的build方法中没有使用ShareDataWidget的数据,那它就没有依赖ShareDataWidget,它的didChangeDependencies()也将不会被调用。

    如果不想在DataWidget发生变化时调用__TestWidgetState的didChangeDependencies()方法,我们可以把inheritFromWidgetOfExactType()方法换成ancestorInheritedElementForWidgetOfExactType():

    static DataWidget of(BuildContext context) {
      //return context.inheritFromWidgetOfExactType(ShareDataWidget);
      return context.ancestorInheritedElementForWidgetOfExactType(ShareDataWidget).widget;
    }
    

    这两个方法有什么区别呢?看源码:

    @override
    InheritedElement ancestorInheritedElementForWidgetOfExactType(Type targetType) {
      final InheritedElement ancestor = _inheritedWidgets == null ? null :  _inheritedWidgets[targetType];
      return ancestor;
    }
    
    @override
    InheritedWidget inheritFromWidgetOfExactType(Type targetType, { Object aspect }) {
      final InheritedElement ancestor = _inheritedWidgets == null ? null :   _inheritedWidgets[targetType];
      //多出的部分
      if (ancestor != null) {
        assert(ancestor is InheritedElement);
        return inheritFromElement(ancestor, aspect: aspect);
      }
      _hadUnsatisfiedDependencies = true;
      return null;
    }
    

    可以看出两者的区别是:inheritFromWidgetOfExactType() 多调了inheritFromElement方法,该方法实现如下:

    @override
    InheritedWidget inheritFromElement(InheritedElement ancestor, { Object aspect }) {
      //注册依赖关系
      _dependencies ??= HashSet<InheritedElement>();
      _dependencies.add(ancestor);
      ancestor.updateDependencies(this, aspect);
      return ancestor.widget;
    }
    

    从该方法实现中,可以看出,ancestor被加到依赖数组_dependencies中,从而注册了依赖关系,也就是说使用ancestorInheritedElementForWidgetOfExactType方法不会有依赖,自然就不会调用__TestWidgetState的didChangeDependencies()方法了。

    二. 使用InheritedWidget实现Provider

    上一篇文章中说到,可以使用Provider来实现全局状态管理,它 是基于InheritedWidget来实现状态共享的。那接下来简单使用InheritedWidget实现一个Provider。

    相关文章

      网友评论

          本文标题:[Flutter开发]06.使用InheritedWidget实

          本文链接:https://www.haomeiwen.com/subject/flxmzqtx.html