Flutter小记

本文详细解析了Flutter中的三个核心树结构:Widget树、Element树和渲染树。讲解了它们的创建、更新和生命周期管理,以及BuildContext的作用。重点阐述了StatefulWidget的setState与元素更新机制,以及AppLifecycleState在不同场景下的表现。
摘要由CSDN通过智能技术生成
1 三种树

Flutter的UI系统包含三棵树:Widget树、Element树、渲染树。他们的依赖关系是:Element树根据Widget树生成,而渲染树又依赖于Element树。

Widget 是 Flutter 功能的抽象描述,是视图的配置信息,同样也是数据的映射。

Widget有个createElement方法,返回一个Element对象(StatelessElement或StatefulElement),并将widget做为参数保存到自己的_widget属性,如果StatefulElement类型的会调用widget的.createState(),将结果保存为_state属性。
Widget(RenderObjectToWidgetAdapter)有个createRenderObject方法,返回一个RenderObject对象保存到_renderObject属性中。

2 加载过程

现在我们重点看一下Element,Element的生命周期如下:

1、Framework 调用Widget.createElement 创建一个Element实例,记为element,widget将作为element的初始化配置。
2、Framework 调用 element.mount(parentElement,newSlot) ,mount方法中首先调用element所对应Widget的createRenderObject方法创建与element相关联的RenderObject对象,然后调用widget.attachRenderObject(此处的widget并非stateless和statefulWidget)方法将element.renderObject添加到渲染树中插槽指定的位置(这一步不是必须的,一般发生在Element树结构发生变化时才需要重新attach)。插入到渲染树后的element就处于“active”状态,处于“active”状态后就可以显示在屏幕上了(可以隐藏)。
3、当有父Widget的配置数据改变时,同时其State.build返回的Widget结构与之前不同,此时就需要重新构建对应的Element树。为了进行Element复用,在Element重新构建前会先尝试是否可以复用旧树上相同位置的element,element节点在更新前都会调用其对应Widget的canUpdate方法,如果返回true,使用新Widget配置数据更新旧的Elemen,否则会创建一个新的Element。Widget.canUpdate主要是判断newWidget与oldWidget的runtimeType和key是否同时相等,如果同时相等就返回true,否则就会返回false。根据这个原理,当我们需要强制更新一个Widget时,可以通过指定不同的Key来避免复用。
4、当有祖先Element决定要移除element 时(如Widget树结构发生了变化,导致element对应的Widget被移除),这时该祖先Element就会调用deactivateChild 方法来移除它,移除后element.renderObject也会被从渲染树中移除,然后Framework会调用element.deactivate 方法,这时element状态变为“inactive”状态。
5、“inactive”态的element将不会再显示到屏幕。为了避免在一次动画执行过程中反复创建、移除某个特定element,“inactive”态的element在当前动画最后一帧结束前都会保留,如果在动画执行结束后它还未能重新变成“active”状态,Framework就会调用其unmount方法将其彻底移除,这时element的状态为defunct,它将永远不会再被插入到树中。
6、如果element要重新插入到Element树的其它位置,如element或element的祖先拥有一个GlobalKey(用于全局复用元素),那么Framework会先将element从现有位置移除,然后再调用其activate方法,并将其renderObject重新attach到渲染树。

3 BuildContext

StatelessWidget和StatefulWidget的build方法都会传一个BuildContext对象:

Widget build(BuildContext context) {}

我们也知道,在很多时候我们都需要使用这个context 做一些事,比如:

Theme.of(context) //获取主题
Navigator.push(context, route) //入栈新路由
Localizations.of(context, type) //获取Local
context.size //获取上下文大小
context.findRenderObject() //查找当前或最近的一个祖先RenderObject

这是一个抽象接口类,有widget等属性,而build调用是发生在StatelessElement和StatefulElement的build方法中。

class StatelessElement extends ComponentElement {
  ...
  @override
  Widget build() => widget.build(this);
  ...
}

发现build传递的参数是this,很明显!这个BuildContext就是StatelessElement/StatefulElement的一个对象。即createElement方法产生的element,我们同样发现StatefulWidget的context是StatefulElement。但StatelessElement和StatefulElement本身并没有实现BuildContext接口,它们是间接继承自Element类,在Element类实现了BuildContext接口

class Element extends DiagnosticableTree implements BuildContext {
    ...
}

即BuildContext就是Widget对应的Element,所以我们可以通过context在StatelessWidget和StatefulWidget的build方法中直接访问Element对象。比如我们获取主题数据的代码Theme.of(context)内部正是调用了Element的dependOnInheritedWidgetOfExactType()方法。

4 更新

对于StatefulWIdget来说,通常会有一个类继承State,定义如下:

abstract class State<T extends StatefulWidget> with Diagnosticable {
}

需要更新内容时,调用setState方法,参数是一个block,在里面block会先执行, 然后再调用

_element.markNeedsBuild()

其内部设置_dirty = true;

5 生命周期

AppLifecycleState共有resumed、inactive、paused、detached四种。
以iOS平台为例,从Native进入Flutter,是resumed,App回到后台,先inactive然后paused。App回到前台,先inactive然后resumed。返回到Native,先inactive,然后paused,最后detached。

An instantiation of a [Widget] at a particular location in the tree.

Widgets describe how to configure a subtree but the same widget can be used
to configure multiple subtrees simultaneously because widgets are immutable.
An [Element] represents the use of a widget to configure a specific location
in the tree. Over time, the widget associated with a given element can
change, for example, if the parent widget rebuilds and creates a new widget
for this location.
///
Elements form a tree. Most elements have a unique child, but some widgets
(e.g., subclasses of [RenderObjectElement]) can have multiple children.

Elements have the following lifecycle:

  • The framework creates an element by calling [Widget.createElement] on the
    widget that will be used as the element’s initial configuration.
  • The framework calls [mount] to add the newly created element to the tree
    at a given slot in a given parent. The [mount] method is responsible for
    inflating any child widgets and calling [attachRenderObject] as
    necessary to attach any associated render objects to the render tree.
  • At this point, the element is considered “active” and might appear on
    screen.
  • At some point, the parent might decide to change the widget used to
    configure this element, for example because the parent rebuilt with new
    state. When this happens, the framework will call [update] with the new
    widget. The new widget will always have the same [runtimeType] and key as
    old widget. If the parent wishes to change the [runtimeType] or key of
    the widget at this location in the tree, it can do so by unmounting this
    element and inflating the new widget at this location.
  • At some point, an ancestor might decide to remove this element (or an
    intermediate ancestor) from the tree, which the ancestor does by calling
    [deactivateChild] on itself. Deactivating the intermediate ancestor will
    remove that element’s render object from the render tree and add this
    element to the [owner]'s list of inactive elements, causing the framework
    to call [deactivate] on this element.
  • At this point, the element is considered “inactive” and will not appear
    on screen. An element can remain in the inactive state only until
    the end of the current animation frame. At the end of the animation
    frame, any elements that are still inactive will be unmounted.
  • If the element gets reincorporated into the tree (e.g., because it or one
    of its ancestors has a global key that is reused), the framework will
    remove the element from the [owner]'s list of inactive elements, call
    [activate] on the element, and reattach the element’s render object to
    the render tree. (At this point, the element is again considered “active”
    and might appear on screen.)
  • If the element does not get reincorporated into the tree by the end of
    the current animation frame, the framework will call [unmount] on the
    element.
  • At this point, the element is considered “defunct” and will not be
    incorporated into the tree in the future.

10.7

请添加图片描述

10.9
请添加图片描述

10.8
请添加图片描述

10 请添加图片描述
请添加图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值