Flutter 的核心设计思想便是“一切皆 Widget”
前言:
Widget 渲染过程
通常情况下,都会用到视图树(View Tree)的概念。而 Flutter 将视图树的概念进行了扩展,把视图数据的组织和渲染抽象为三部分,即 Widget,Element 和 RenderObject。
Widget
Widget 是 Flutter 世界里对视图的一种结构化描述,你可以把它看作是前端中的“控件”或“组件”。Widget 是控件实现的基本逻辑单位,里面存储的是有关视图渲染的配置信息,包括布局、渲染属性、事件响应信息等。
Element
Element 是 Widget 的一个实例化对象,它承载了视图构建的上下文数据,是连接结构化的配置信息到完成最终渲染的桥梁。
Flutter 渲染过程,可以分为这么三步:
- 首先,通过 Widget 树生成对应的 Element 树;
- 然后,创建相应的 RenderObject 并关联到 Element.renderObject 属性上;
- 最后,构建成 RenderObject 树,以完成最终的渲染。
RenderObject
RenderObject 是主要负责实现视图渲染的对象。
Flutter 通过控件树(Widget 树)中的每个控件(Widget)创建不同类型的渲染对象,组成渲染对象树。
渲染对象树在 Flutter 的展示过程分为四个阶段,即布局、绘制、合成和渲染。 其中,布局和绘制在 RenderObject 中完成,Flutter 采用深度优先机制遍历渲染对象树,确定树中各个对象的位置和尺寸,并把它们绘制到不同的图层上。绘制完毕后,合成和渲染的工作则交给 Skia 搞定
扩展知识:
React:JSX->虚拟DOM->浏览器DOM
React Native:JSX->虚拟DOM->Android/iOS原生控件
flutter:Widget->Element(类似虚拟DOM,只是一种数据结构)-> RenderObject 交给底层渲染
Element是可复用的,只要Widget前后类型一样。比如Widget是蓝色的,重建后变红色了,Element是会复用的。所以是多个Widget(销毁前后)会对应一个Element。而Widget是不可变的,一旦改变就会销毁重建。
————————————————
state生命周期
————————————————
widget是immutable的,发生变化的时候需要重建,所以谈不上状态。StatefulWidget 中的状态保持其实是通过State类来实现的。State拥有一套自己的生命周期,下面做一个简单的介绍。
State 的生命周期可以分为 3 个阶段:
- 创建(插入视图树)
- 更新(在视图树中存在)
- 销毁(从视图树中移除)
创建
State 初始化时会依次执行 :构造方法 -> initState -> didChangeDependencies -> build,随后完成页面渲染。
构造函数(construction)
调用次数:1次
用处:
页面传参
-
初始化UI配置
构造方法是 State 生命周期的起点,Flutter 会通过调用 StatefulWidget.createState() 来创建一个 State。我们可以通过构造方法,来接收父 Widget 传递的初始化 UI 配置数据。这些配置数据,决定了 Widget 最初的呈现效果。
这个函数严格意义上来讲不属于生命周期的一部分,因为这个时候State的widget属性为空,无法在构造函数中访问widget的属性 。但是构造函数必然是要第一个调用的。可以在这一部分接收前一个页面传递过来的数据。
initState
调用次数:1次
用处:
-
初始化变量
当State 对象被插入渲染树的时候调用,这个函数在生命周期中只调用一次。所以我们可以在这里做一些初始化工作,比如初始化State的变量、状态变量设定默认值。
didChangeDependencies
调用次数:多次
- 初始化时,在initState()之后立刻调用
- 当依赖的InheritedWidget rebuild,会触发此接口被调用
Called when a dependency of this [State] object changes.
build
调用次数:多次
作用是构建视图。经过以上步骤,Framework 认为 State 已经准备好了,于是调用 build。我们需要在这个函数中,根据父 Widget 传递过来的初始化配置数据,以及 State 的当前状态,创建一个 Widget 然后返回。
Called whenever the widget configuration changes.
更新
Widget 的状态更新,主要由 3 个方法触发:
- setState
- didchangeDependencies
- didUpdateWidget
setState:我们最熟悉的方法之一。当状态数据发生变化时,我们总是通过调用这个方法告诉 Flutter:“我这儿的数据变啦,请使用更新后的数据重建 UI!”;当setState触发的时候build会再次被调用。
didChangeDependencies:State 对象的依赖关系发生变化后,Flutter 会回调这个方法,随后触发组件构建。哪些情况下 State 对象的依赖关系会发生变化呢?典型的场景是,系统语言 Locale 或应用主题改变时,系统会通知 State 执行 didChangeDependencies 回调方法。
didUpdateWidget:当 Widget 的配置发生变化时,比如,父 Widget 触发重建(即父 Widget 的状态发生变化时),热重载时,系统会调用这个函数。
一旦这三个方法被调用,Flutter 随后就会销毁老 Widget,并调用 build 方法重建 Widget。
销毁(组件移除)
组件移除,例如页面销毁的时候会依次执行:deactivate > dispose
deactivate
调用次数:多次
Called when this object is removed from the tree.
在dispose之前,会调用这个函数。实测在组件可见状态变化的时候会调用,当组件卸载时也会先一步dispose调用。值得注意的是,页面切换时,由于 State 对象在视图树中的位置发生了变化,需要先暂时移除后再重新添加,重新触发组件构建,因此这个函数也会被调用。
dispose
调用次数:1次
Called when this object is removed from the tree permanently.
一旦到这个阶段,组件就要被销毁了,这个函数一般会移除监听,清理环境。
// 初始化状态,只执行一次
void initState() { }
// 当widget依赖的对象发生变化时调用,例如全局的主题或语言
void didChangeDependencies() { }
// 当widget重新构建时调用,根据canUpdate方法判断
void didUpdateWidget(covariant T oldWidget) { }
// 构建UI
void build() { }
// 调试模式下,热重载执行,Release模式不会执行
void reassemble() { }
// 当widget被移除时调用
void deactivate() { }
// 当widget被永久移除时,可以做释放资源
void dispose() { }
————————————————
App 生命周期
————————————————
在原生开发中,我们可以通过重写 Activity、ViewController 生命周期回调方法,或是注册应用程序的相关通知,来监听 App 的生命周期并做相应的处理。而在 Flutter 中,我们可以利用 WidgetsBindingObserver 类,来实现同样的需求。
通过继承WidgetsBindingObserver,该类中存在一个回调方法didChangeAppLifecycleState,用于表示当前应用状态改变。(当然WidgetsBindingObserver 中还有其他很多状态回调,比如一个route 被push 或者pop,比如横竖屏变化,比如用户locales切换,是否存在内存压力,辅助功能切换等。)
常用状态包含如下几个:
名称 | 状态 |
---|---|
resumed | 可见并能响应用户的输入 |
inactive | 处在并不活动状态,无法处理用户响应 |
paused | 不可见并不能响应用户的输入,但是在后台继续活动中 |
abstract class WidgetsBindingObserver {
//页面pop
Future didPopRoute() => Future.value(false);
//页面push
Future didPushRoute(String route) => Future.value(false);
//系统窗口相关改变回调,如旋转
void didChangeMetrics() { }
//文本缩放系数变化
void didChangeTextScaleFactor() { }
//系统亮度变化
void didChangePlatformBrightness() { }
//本地化语言变化
void didChangeLocales(List locale) { }
//App生命周期变化
void didChangeAppLifecycleState(AppLifecycleState state) { }
//内存警告回调
void didHaveMemoryPressure() { }
//Accessibility相关特性回调
void didChangeAccessibilityFeatures() {}
}