Flutter视图原理之StatefulWidget,InheritedWidget

这篇具有很好参考价值的文章主要介绍了Flutter视图原理之StatefulWidget,InheritedWidget。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

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

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

StatefulElement

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

Flutter视图原理之StatefulWidget,InheritedWidget,flutter,android,dart) Flutter视图原理之StatefulWidget,InheritedWidget,flutter,android,dart)

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);
  }
  //...省略
}

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

2. build

使用state来创建widget:

  Widget build() => state.build(this);

3. _firstBuild

void _firstBuild() {
   //...省略
    state.didChangeDependencies();
    assert(() {
      state._debugLifecycleState = _StateLifecycle.ready;
    }());
    super._firstBuild();
  }

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

再看这幅图:
Flutter视图原理之StatefulWidget,InheritedWidget,flutter,android,dart

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();
  }

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();
  }

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

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

InheritedElement

InheritedWidget本质有两大功能,

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

这些功能都是在Element和InheritedElement中实现的。
Flutter视图原理之StatefulWidget,InheritedWidget,flutter,android,dartFlutter视图原理之StatefulWidget,InheritedWidget,flutter,android,dart

1. Element类

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

  PersistentHashMap<Type, InheritedElement>? _inheritedElements;
  Set<InheritedElement>? _dependencies;
  bool _hadUnsatisfiedDependencies = false;

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

在当前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);
  }

从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);
  }

然后在系统的buttonWidget中使用了这个主题:
Flutter视图原理之StatefulWidget,InheritedWidget,flutter,android,dart

我们接着看下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. 从_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;
  }

首先是将祖先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;
  }

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

4. InheritedWidget的状态绑定

4.1. ProxyElement

proxyElement复写了两个接口:

widget的创建是返回的子child

  
  Widget build() => (widget as ProxyWidget).child;

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

  
  void update(ProxyWidget newWidget) {
    final ProxyWidget oldWidget = widget as ProxyWidget;

    super.update(newWidget);

    updated(oldWidget);
    
    rebuild(force: true);
  }

proxyElement新增了两个接口:

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

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. 先判断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();
  }

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

statefulElemenr实现:

  bool _didChangeDependencies = false;

  
  void didChangeDependencies() {
    super.didChangeDependencies();
    _didChangeDependencies = true;
  }

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

inheritedElement功能流程图:

Flutter视图原理之StatefulWidget,InheritedWidget,flutter,android,dart文章来源地址https://www.toymoban.com/news/detail-723307.html

到了这里,关于Flutter视图原理之StatefulWidget,InheritedWidget的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Flutter StatefulWidget传递数据,多级控件传递数据

    在Flutter中,StatefulWidget可以通过构造函数将数据传递给其子控件,这种方式适用于一些简单的场景。但是,当存在多级嵌套控件时,将数据从祖先传递到后代可能会变得困难。在这种情况下,可以使用Flutter提供的InheritedWidget类来传递数据。 通过构造函数传递数据 在StatefulW

    2024年02月10日
    浏览(31)
  • Flutter关于StatefulWidget中State刷新时机的一点实用理解

    刚入门flutter开发,使用StatefulWidget踩了很多坑,就我遇到典型问题谈谈见解。 1.initState方法只会在控件初始化的时候执行一遍。 2.控件内部执行setState方法,则会每次执行build方法。 3.控件销毁会执行dispose方法,所以一些未释放的资源可以在该方法中执行,例如计时器。 4.使

    2024年02月10日
    浏览(39)
  • flutter:占位视图(骨架屏、shimmer)

    有时候打开美团,在刚加载数据时会显示一个占位视图,如下: 那么这个是如何实现的呢?我们可以使用 shimmer 来开发该功能 官方文档 https://pub-web.flutter-io.cn/packages/shimmer 安装 示例1 示例2

    2024年02月14日
    浏览(47)
  • 【数据库原理】(13)视图的使用

    视图(View) 是一种虚拟的表,它的内容由查询定义,但不存储实际的数据。视图是从一个或多个基本表(或其他视图)生成的。 特点: 虚拟性 :视图不包含数据本身,仅保存了一个查询,这个查询在每次访问视图时动态地执行。 动态性 :视图显示的数据是从基本表中实时

    2024年02月02日
    浏览(36)
  • 数据库原理及应用实验报告-实验4-SQL的视图

    实验目的 熟悉SQL支持的有关视图的操作,能够熟练使用SQL语句来创建需要的视图,对视图进行查询和删除视图。 实验内容 定义常见的视图形式,包括: 行列子集视图 WITH CHECK OPTION的视图 基于多个基表的视图 基于视图的视图 带表达式的视图 分组视图 通过实验考察WITH CHEC

    2023年04月26日
    浏览(51)
  • 【三维重建】DreamGaussian:高斯splatting的单视图3D内容生成(原理+代码)

    项目主页:https://dreamgaussian.github.io/ (包含论文和代码) 提示:以下是本篇文章正文内容,下面案例可供参考 常用的3D内容创建方式,主要是 利用基于优化的通过分数蒸馏采样(SDS)进行的3D生成 。该方法每个样本优化较慢,很难实际应用。本文提出了DreamGaussian,兼顾效率

    2024年02月06日
    浏览(47)
  • 数据库系统原理及MySQL应用教程实验六视图创建与管理

    1.理解视图的概念。 2.掌握创建、更改、删除视图的方法。 3.掌握使用视图来访问数据的方法。 1.验证性实验:在job数据库中,有聘任人员信息表:Work_lnfo表对其视图的操作。 2.设计性试验:在学生管理系统中,有学生信息表studentinfo表对其视图的操作。 (一)验证性实验 在

    2024年02月04日
    浏览(81)
  • Flutter 笔记 | Flutter 核心原理(三)布局(Layout )过程

    Layout(布局)过程主要是确定每一个组件的布局信息(大小和位置),Flutter 的布局过程如下: 父节点向子节点传递约束(constraints)信息,限制子节点的最大和最小宽高。 子节点根据约束信息确定自己的大小(size)。 父节点根据特定布局规则(不同布局组件会有不同的布

    2024年02月09日
    浏览(86)
  • Flutter原理篇:GestureDetector原理深度剖析及手势原理(上)

    今天我们来讲讲GestureDetector的深度剖析,只有了解原理了,才能知道手势冲突如何解决以及如何更灵活的运用手势。 我们先来看看GestureDetector的内部结构 1. GestureDetector 只是一个包装类,最终还是由 Listener 的 RenderPointListener 执行事件的操作 2.点击事件开始时会首先执行 RawG

    2024年04月27日
    浏览(36)
  • Angular 视图数据模型变化导致重新计算模板中的表达式的技术原理

    在深入探讨 Angular 中的数据绑定和视图更新机制之前,重要的是要理解 Angular 的核心功能之一:变更检测(Change Detection)。变更检测是 Angular 框架用来同步模型(数据)和视图(模板)的过程。当数据模型变化时,Angular 会重新计算模板中的表达式,并将任何变化反映到视图

    2024年04月22日
    浏览(38)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包