美文网首页
Flutter: InheritedWidget作用简介

Flutter: InheritedWidget作用简介

作者: AryCode | 来源:发表于2022-12-27 11:15 被阅读0次

    1.共享数据

    image_15.png

    1.1 Normal Widget Element Mount

    void mount(Element? parent, Object? newSlot) {
      ... 
      _updateInheritance();
      ...
    }
      void _updateInheritance() {
        _inheritedWidgets = _parent?._inheritedWidgets;#备注①
      }
    

    1.2 InheritedWidget Element Mount

    void mount(Element? parent, Object? newSlot) {
      ... 
      _updateInheritance();
      ...
    }
    void _updateInheritance() {
        final Map<Type, InheritedElement>? incomingWidgets = _parent?._inheritedWidgets;
        if (incomingWidgets != null)
          _inheritedWidgets = HashMap<Type, InheritedElement>.of(incomingWidgets);#备注②
        else
          _inheritedWidgets = HashMap<Type, InheritedElement>();
        _inheritedWidgets![widget.runtimeType] = this;#备注③
      }
    

    结论:

    • 1.通过备注①知道:普通Widget(不是InheritedWidget) _inheritedWidgets属性值是通过继承传递下去的
    • 2.通过备注②知道:InheritedWidget _inheritedWidgets属性值会深拷贝父节点传递过来的值
    • 3.通过备注②③知道:当出现相同的widget.runtimeType,最近的InheritedWidget会覆盖祖先节点的数据
    • InheritedElement随着_inheritedWidgets传递给各个子孙节点,由于是同样的数据(没有相同的widget.runtimeType覆盖),就实现了数据共享

    2.页面刷新

    (Element Class)
    void update(ProxyWidget newWidget) {①
        ...
        updated(oldWidget);
        ...
    }
    
    (InheritedElement Class)
    void updated(InheritedWidget oldWidget) {②
        if ((widget as InheritedWidget).updateShouldNotify(oldWidget))
          super.updated(oldWidget);
    }
    
    (ProxyElement Class)
    @protected
    void updated(covariant ProxyWidget oldWidget) {③
       notifyClients(oldWidget);
    }
    
    (ProxyElement Class)
      @override
    void notifyClients(InheritedWidget oldWidget) {④
        for (final Element dependent in _dependents.keys) {
          notifyDependent(oldWidget, dependent);
        }
      }
    
    (InheritedElement Class)
    @protected
      void notifyDependent(covariant InheritedWidget oldWidget, Element dependent) {⑦
        dependent.didChangeDependencies();
      }
    
    (Element Class)
    @mustCallSuper
      void didChangeDependencies() {⑧
        markNeedsBuild();
      }
    继承链  InheritedElement -> ProxyElement -> Element
    

    参考如下代码:

    class ShareDataWidget extends InheritedWidget {
      const ShareDataWidget({
        Key? key,
        required this.data,
        required Widget child,
      }) : super(key: key, child: child);
    
      final int data; //需要在子树中共享的数据,保存点击次数
    
      //定义一个便捷方法,方便子树中的widget获取共享数据
      static ShareDataWidget? of(BuildContext context) {
        return context.dependOnInheritedWidgetOfExactType<ShareDataWidget>();
      }
    
      //该回调决定当data发生变化时,是否通知子树中依赖data的Widget重新build
      @override
      bool updateShouldNotify(ShareDataWidget old) {
        return old.data != data;
      }
    }
    
    class MyHomePage extends StatefulWidget {
      @override
      State<MyHomePage> createState() => _MyHomePageState();
    }
    
    class _MyHomePageState extends State<MyHomePage> {
      int count = 0;
    
      @override
      void didChangeDependencies() {
        // TODO: implement didChangeDependencies
        super.didChangeDependencies();
      }
    
      @override
      Widget build(BuildContext context) {
        return ShareDataWidget(
          data: count,
          child: GestureDetector(
            onTap: () {
              setState(() {
                count++;
              });
            },
          ),
        );
      }
    }
    

    当我们调用setState 方法后,接下来会先调用方法①(这里的入口代码主要是根据Widget的更新原理,具体需要自己参考源码),接着会调用方法②,紧接着会调用方法③,这是根据继承链得来的

    这里主要关注下②中的updateShouldNotify,一般当数据发生改变时候,返回true,才会调用③,通知所有的监听Element,刷新他们,根据第④个方法知道,现在的问题是InheritedElement_dependents是如何将数据填入的

    (InheritedElement Class)
    @protected
     void setDependencies(Element dependent, Object? value) {
       _dependents[dependent] = value;
     }
    

    根据上面的代码,现在只要知道是谁调用它就OK,答案就是dependOnInheritedWidgetOfExactType,如上面的ShareDataWidget中的代码

    static ShareDataWidget? of(BuildContext context) {
       return context.dependOnInheritedWidgetOfExactType<ShareDataWidget>();
    }
    

    根据函数调用链,得知:

      @override
      T? dependOnInheritedWidgetOfExactType<T extends InheritedWidget>({Object? aspect}) {⑤
        final InheritedElement? ancestor = _inheritedWidgets == null ? null : _inheritedWidgets![T];
        if (ancestor != null) {
          return dependOnInheritedElement(ancestor, aspect: aspect) as T;
        }
        _hadUnsatisfiedDependencies = true;
        return null;
      }
    
      @override
      InheritedWidget dependOnInheritedElement(InheritedElement ancestor, { Object? aspect }) {⑥
        assert(ancestor != null);
        _dependencies ??= HashSet<InheritedElement>();
        _dependencies!.add(ancestor);
        ancestor.updateDependencies(this, aspect);
        return ancestor.widget as InheritedWidget;
      }
    

    查找_inheritedWidgets是否有对应类型的InheritedWidget如果没有,直接返回 ,如果找到,根据代码⑥中的
    ancestor.updateDependencies(this, aspect);这个方法,会将当前的Element加入到InheritedElemnt中的监听中

    @protected
      void updateDependencies(Element dependent, Object? aspect) {
        setDependencies(dependent, null);
      }
    

    InheritedWidget需要执行update的时候,就会通知_dependencies中所有的Element执行刷新操作

    相关文章

      网友评论

          本文标题:Flutter: InheritedWidget作用简介

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