• Flutter视图原理之StatefulWidget,InheritedWidget


    flutter项目中,StatelessWidget,StatefulWidget,InheritedWidget是常见的widget,今天通过源码分析下它们是怎么实现的。

    对应的功能基本上都是在element中实现的,widget只是提供组件配置的作用,所以在讲解StatefulWidget,InheritedWidget的时候,主要还是分析对应的element的实现。

    StatefulElement

    StatefulWidget是带有状态的Widget,和statelessWidget不同,Widget的创建是委托给state创建的,而不是使用widget.build直接创建的。
    StatelessWidget代码上一章 三棵树的建立过程 已经讲过了,忽略。

    在这里插入图片描述) 在这里插入图片描述)

    1. 构造函数

    对比statelessElement的构造函数:

    class StatefulElement extends ComponentElement {
      /// Creates an element that uses the given widget as its configuration.
      StatefulElement(StatefulWidget widget)
          : _state = widget.createState(),
            super(widget) {
        assert(state._element == null);
        state._element = this;
    
        state._widget = widget;
        assert(state._debugLifecycleState == _StateLifecycle.created);
      }
      //...省略
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    在构造函数中,直接调用了widget.createState().新建了state,state的成员变量有element,widget都是私有成员,这个时候state的生命周期应该是created的,state._debugLifecycleState == _StateLifecycle.created

    2. build

    使用state来创建widget:

      Widget build() => state.build(this);
    
    
    • 1
    • 2

    3. _firstBuild

    void _firstBuild() {
       //...省略
        state.didChangeDependencies();
        assert(() {
          state._debugLifecycleState = _StateLifecycle.ready;
        }());
        super._firstBuild();
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    这个函数调用,发生在第一次生成element的时候,该element被mount的时候触发,这个时候会回调state的didChangeDependencies方法。
    还有一种情况是performRebuild()的时候有可能会回调,下面会讲到。

    再看这幅图:
    在这里插入图片描述

    3. didChangeDependencies

    didChangeDependencies函数是element的回调接口,这个接口是在依赖项更改的时候被parent通知调用的,会修改_didChangeDependencies = true;,然后performRebuild()函数会触发state.didChangeDependencies();的回调。

      bool _didChangeDependencies = false;
    
      
      void didChangeDependencies() {
        super.didChangeDependencies();
        _didChangeDependencies = true;
      }
    
      
      void performRebuild() {
        if (_didChangeDependencies) {
          state.didChangeDependencies();
          _didChangeDependencies = false;
        }
        super.performRebuild();
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    4. setState

    void setState(VoidCallback fn) {
    	//。。。省略
        final Object? result = fn() as dynamic;
        assert(() {
          if (result is Future) {
            throw FlutterError.fromParts(<DiagnosticsNode>[]);
          }
          return true;
        }());
        _element!.markNeedsBuild();
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    可以看到setstate的参数不能是返回future的回调,然后调用element的markNeedsBuild方法,通知element重新build一次。
    重新build的时候,如果满足element可以复用旧的,但是需要更新newWidget的情况下,会触发state.didUpdateWidget(方法,也就对应的上图生命周期了。

    注:上一章 三棵树的建立过程 已经讲过其他的函数,这里忽略。

    InheritedElement

    InheritedWidget本质有两大功能,

    1. InheritedWidget数据向下传递(下层节点可以获取到InheritedWidget中的数据)
    2. InheritedWidget的状态绑定(就是InheritedWidget被修改,会导致引用的地方数据刷新)

    这些功能都是在Element和InheritedElement中实现的。
    在这里插入图片描述在这里插入图片描述

    1. Element类

    element是基类,是所有子类的基础实现,它内部有这几个成员变量让inheritedElement功能得以实现:

      PersistentHashMap<Type, InheritedElement>? _inheritedElements;
      Set<InheritedElement>? _dependencies;
      bool _hadUnsatisfiedDependencies = false;
    
    • 1
    • 2
    • 3

    _inheritedElements这个保存着这棵树的所有inheritedElement类型元素(如果自己也是,那么自己也会加入到这个集合中);
    _dependencies保存着当前element所依赖的祖先InheritedElement;
    _hadUnsatisfiedDependencies 当按照类型查找祖先InheritedElement没找到,那么这个变量会设置成true,下次active激活页面的时候,会通知build一次。

    2. _updateInheritance

    element的实现如下:

      void mount(Element? parent, Object? newSlot) {
    	//省略
        _updateInheritance();
        attachNotificationTree();
      }
      
      void _updateInheritance() {
        assert(_lifecycleState == _ElementLifecycle.active);
        _inheritedElements = _parent?._inheritedElements;
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    在当前element挂载到树中的时候,会主动更新一次_inheritedElements 元素,从parent当中获取_inheritedElements 集合。

    InheritedElement复写了这个方法:

      void _updateInheritance() {
        assert(_lifecycleState == _ElementLifecycle.active);
        final PersistentHashMap<Type, InheritedElement> incomingWidgets =
            _parent?._inheritedElements ?? const PersistentHashMap<Type, InheritedElement>.empty();
        _inheritedElements = incomingWidgets.put(widget.runtimeType, this);
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    从parent获取了_inheritedElements 集合之后,还需要将自己加入到这个集合中。

    3. InheritedWidget数据向下传递

    3.1 dependOnInheritedWidgetOfExactType

    dependOnInheritedWidgetOfExactType这个方法,通常使用的情况是,子widget的state中去获取全局的element。如下,获取这个主题元素CupertinoThemeData 的时候,调用了of方法,里面就是调用的dependOnInheritedWidgetOfExactType。

      static CupertinoThemeData of(BuildContext context) {
        final _InheritedCupertinoTheme? inheritedTheme = context.dependOnInheritedWidgetOfExactType<_InheritedCupertinoTheme>();
        return (inheritedTheme?.theme.data ?? const CupertinoThemeData()).resolveFrom(context);
      }
    
    • 1
    • 2
    • 3
    • 4

    然后在系统的buttonWidget中使用了这个主题:
    在这里插入图片描述

    我们接着看下dependOnInheritedWidgetOfExactType到底做了什么事情:

      
      T? dependOnInheritedWidgetOfExactType<T extends InheritedWidget>({Object? aspect}) {
        assert(_debugCheckStateIsActiveForAncestorLookup());
        final InheritedElement? ancestor = _inheritedElements == null ? null : _inheritedElements![T];
        if (ancestor != null) {
          return dependOnInheritedElement(ancestor, aspect: aspect) as T;
        }
        _hadUnsatisfiedDependencies = true;
        return null;
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    1. 从_inheritedElements集合中,按照key来查询element元素;
    2. 查找到目标ancestor之后,需要和祖先进行关系绑定dependOnInheritedElement
    3. 如果查找不到,那么将_hadUnsatisfiedDependencies 置为true。

    dependOnInheritedElement

      
      InheritedWidget dependOnInheritedElement(InheritedElement ancestor, { Object? aspect }) {
        _dependencies ??= HashSet<InheritedElement>();
        _dependencies!.add(ancestor);
        ancestor.updateDependencies(this, aspect);
        return ancestor.widget as InheritedWidget;
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    首先是将祖先element加入到_dependencies集合中,这样该element就知道自己依赖了哪几个祖先element,祖先也调用updateDependencies更新祖先的依赖,然后将祖先返回。

    updateDependencies

    祖先是inheritedElement,它实现了updateDependencies方法,将子element加入到map中。

      final Map<Element, Object?> _dependents = HashMap<Element, Object?>();
    
      void updateDependencies(Element dependent, Object? aspect) {
        setDependencies(dependent, null);
      }
      
      void setDependencies(Element dependent, Object? value) {
        _dependents[dependent] = value;
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    通过上面的步骤,子element和祖先inheritedElement,产生了相互依赖关系。
    实现了InheritedWidget数据向下传递(下层节点可以获取到InheritedWidget中的数据)

    4. InheritedWidget的状态绑定

    4.1. ProxyElement

    proxyElement复写了两个接口:

    widget的创建是返回的子child

      
      Widget build() => (widget as ProxyWidget).child;
    
    
    • 1
    • 2
    • 3

    update更新widget,需要先调用updated,在调用build方法

      
      void update(ProxyWidget newWidget) {
        final ProxyWidget oldWidget = widget as ProxyWidget;
    
        super.update(newWidget);
    
        updated(oldWidget);
        
        rebuild(force: true);
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    proxyElement新增了两个接口:

    	//widget已经更新过了,需要通知依赖项
      void updated(covariant ProxyWidget oldWidget) {
        notifyClients(oldWidget);
      }
      
      //通知接口,具体需要子类实现
      void notifyClients(covariant ProxyWidget oldWidget);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    inheritedElement复写接口:

      void updated(InheritedWidget oldWidget) {
        if ((widget as InheritedWidget).updateShouldNotify(oldWidget)) {
          super.updated(oldWidget);
        }
      }
    
      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);
        }
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    1. 先判断updateShouldNotify接口需要根据widget来判断,是否通知依赖集合
    2. 如果需要通知,那么遍历_dependents集合,首先验证是否是自己的子孙,接着验证子element._dependencies集合是否有parent;
    3. 接着通知依赖的子项,调用dependent.didChangeDependencies();

    element实现:

      void didChangeDependencies() {
        assert(_lifecycleState == _ElementLifecycle.active); // otherwise markNeedsBuild is a no-op
        assert(_debugCheckOwnerBuildTargetExists('didChangeDependencies'));
        markNeedsBuild();
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    通知子child的element需要重新build了,因为子child依赖了parent的数据,parent的数据发生变化的时候,是需要强制子child去重新build的。

    statefulElemenr实现:

      bool _didChangeDependencies = false;
    
      
      void didChangeDependencies() {
        super.didChangeDependencies();
        _didChangeDependencies = true;
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    除了调用super方法,还将_didChangeDependencies 设置为true;上面说过performRebuild()调用的时候,如果这个标志是true的话,会通知state.didChangeDependencies();接口,对应上了上面图片所示的生命周期函数回调。

    inheritedElement功能流程图:

    在这里插入图片描述

  • 相关阅读:
    Python---继承与复写
    【表白单页模板】纯HTML表白+唯美3D动态相册表白网站制作(html+css+js)
    针对springcloud gateway 跨域问题解决方案
    3.x名称空间详解
    计算机毕业设计ssm+vue基本微信小程序的健康食谱交流共享平台
    阿里云服务器如何关闭防火墙?阿里云安全组怎么设置端口?
    [Django开源学习 1]django-vue-admin
    K8S常用命令(长期更新)
    自然语言推断-PyTorch
    qt6 多媒体开发代码分析(一)
  • 原文地址:https://blog.csdn.net/u012345683/article/details/133927306