美文网首页记录自学flutter点点滴滴
Flutter 学习之旅(二十九) 数据共享 Inherited

Flutter 学习之旅(二十九) 数据共享 Inherited

作者: Tsm_2020 | 来源:发表于2020-08-28 11:04 被阅读0次

    InheritedWidget

    是Flutter 中非常重要的一个功能性组件,他的主要作用是用来提供一种从上到下的一种传递数据的方式,
    在flutter app中的theme 和 local 就是这样写的,

    didChangeDependencies

    这个方法在前面介绍StatefulWidget 的时候介绍过该方法,表示他所依赖的父布局发现变化时这个方法会被framework 调用,但是如何判断父控件是否发生变化,就是根据 子布局是否使用并注册了父布局的 InheritedWidget,这里为什么说是使用并注册,我们会在下面介绍一下,

    先写一个widget , 他含有一个count 属性,

    class TsmShareInteritedWidget extends InheritedWidget {
    
    
      TsmShareInteritedWidget({@required this.data, Widget child}) : super(child: child);
    
      int data;
    
      @override
      bool updateShouldNotify(TsmShareInteritedWidget oldWidget) {
        return oldWidget.data != data;
      }
    
    
      static TsmShareInteritedWidget of(BuildContext context) {
        return context
            .dependOnInheritedWidgetOfExactType<TsmShareInteritedWidget>();
      }
    }
    

    想要使用 TsmShareInteritedWidget 共享出来的count 这个数据的使用方法就是

    TsmShareInteritedWidget.of(context).data;
    

    写一个TsmInheritedWidget 使用共享数据

    class  TsmInheritedWidget extends StatefulWidget{
      @override
      State<StatefulWidget> createState() =>_TsmInheritedWidgetState();
    
    }
    
    
    class _TsmInheritedWidgetState extends State<TsmInheritedWidget>{
      @override
      Widget build(BuildContext context) {
        printString('build');
        return Text(TsmShareInteritedWidget.ofDate(context).data.toString());
      }
    
    
    
      @override
      void didChangeDependencies() {
        super.didChangeDependencies();
        printString('didChangeDependencies');
      }
    
    }
    

    按照上面我的对InheritedWidget 这个控件介绍,他的作用是从上至下传递,也就是 InheritedWidget所共享的这个count 只能由他的子widget使用,最后再来一个使用他的例子

    class _TsmInheritedSendPageState extends State<TsmInheritedSendPage> {
      int count = 0;
    
      @override
      Widget build(BuildContext context) => Scaffold(
            appBar: AppBar(
              title: Text('Inherited 学习'),
            ),
            body: Container(
              child: Center(
                  child: TsmShareInteritedWidget(
                data: count,
                child: Column(
                  mainAxisSize: MainAxisSize.min,
                  children: [
                    TsmInheritedWidget(),
                    SizedBox(
                      height: 15,
                    ),
                    RaisedButton(
                      child: Text('计数器'),
                      onPressed: () {
                        setState(() {
                          count++;
                        });
                      },
                    )
                  ],
                ),
              )),
            ),
          );
    }
    

    点击按钮的时候发现didChangeDependencies 和build 方法同时打印

    那么为什么非要多此一举监听didChangeDependencies,值机监听build方法不就可以了,其实如果数据发生变化是触发请求接口的条件,如果该条件放在build中,则请求接口会发发生的很频繁,而didChangeDependencies 则是可控制的变成,更为合理,(即didChangeDependencies 调用 build必定调用,但是build调用 didChangeDependencies 不一定被调用)

    那么这里如果我只想引用数据不想要didChangeDependencies 回调被调用呢,
    按照上面的说法是,如果想要didChangeDependencies 这个回调被调用必须是使用数据并注册,
    也就是说我们不注册就可以了,

    这里看了一下源码

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

    源码的逻辑就是先找到InheritedElement ,然后再做数据变化依赖,dependOnInheritedElement

    通过搜索final InheritedElement ancestor = _inheritedWidgets == null ? null : _inheritedWidgets[T]; 这句代码找到另一个只是获取数据widgit的方法,他只有获取数据的方法,并没有注册

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

    下面我们在TsmShareInteritedWidget 这添加一个ofData方法,修改TsmInheritedWidget 中的 TsmShareInteritedWidget.of 改成TsmShareInteritedWidget.ofData ,

      static TsmShareInteritedWidget ofDate(BuildContext context) {
        return context
            .getElementForInheritedWidgetOfExactType<TsmShareInteritedWidget>()
            .widget;
      }
    

    修改后的TsmShareInteritedWidget 变化为

    class TsmShareInteritedWidget extends InheritedWidget {
    
    
      TsmShareInteritedWidget({@required this.data, Widget child}) : super(child: child);
    
      int data;
    
      @override
      bool updateShouldNotify(TsmShareInteritedWidget oldWidget) {
        return oldWidget.data != data;
      }
    
      ///  源码中方法
      ///  获取数据同时绑定
      ///
      ///  @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;
      ///    }
      ///
      ///
      static TsmShareInteritedWidget of(BuildContext context) {
        return context
            .dependOnInheritedWidgetOfExactType<TsmShareInteritedWidget>();
      }
    
      /// 只获取数据
      ///
      ///
      ///
      ///源码中的方法
      ///  @override
      ///  InheritedElement getElementForInheritedWidgetOfExactType<T extends InheritedWidget>() {
      ///    assert(_debugCheckStateIsActiveForAncestorLookup());
      ///    final InheritedElement ancestor = _inheritedWidgets == null ? null : _inheritedWidgets[T];
      ///    return ancestor;
      ///  }
      static TsmShareInteritedWidget ofData(BuildContext context) {
        return context
            .getElementForInheritedWidgetOfExactType<TsmShareInteritedWidget>()
            .widget;
      }
    }
    
    ///  总结,通过查看源码发现,dependOnInheritedWidgetOfExactType  依赖关系真正建立的时机是  dependOnInheritedElement  这个方法,除了这个,其实这两个方法都是一样的
    ///
    ///   这就实现了使用   dependOnInheritedWidgetOfExactType  该方法时,不仅可以使用数据,同时在数据发生变更的同时,didChangeDependencies  方法还是调用
    ///   而  getElementForInheritedWidgetOfExactType  这个方法只能使用数据,
    ///
    ///   其实在使用过程中还会发生 如果  didChangeDependencies  方法发生变化,其实build 方法也会被调用,费什么非要多此一举呢,其实如果数据发生变化是触发请求接口的条件
    ///   如果该条件放在build中,则请求接口会发发生的很频繁,而didChangeDependencies 则是可控制的变成,更为合理
    ///
    ///
    

    这里reload代码发现 didChangeDependencies 还是会打印日志,重新打包后就不打印,貌似就是在重新reload的时候,已经注册的方法,没办法取消注册导致的

    其实现在这种用法还是有弊端的,那就是我们只想更新一个TsmInheritedWidget 中的数据,但是调用setState方法时,整个页面都刷新了,这样做不是很好,这个问题我会在下一篇Provider中解决

    我学习flutter的整个过程都记录在里面了
    https://www.jianshu.com/c/36554cb4c804

    最后附上demo 地址

    https://github.com/tsm19911014/tsm_flutter

    相关文章

      网友评论

        本文标题:Flutter 学习之旅(二十九) 数据共享 Inherited

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