1、UI 编程的两种方式
目前主流的UI编写方式主要有两种,命令式UI和声明式UI。在大家所熟悉的iOS中UI编程方式是命令式UI(Imperative UI),而声明式UI的意思就是让我们描述我们需要一个什么样的界面。在Flutter中就是采用了声明式UI(Declarative UI)
下面这样一个简单的例子:
在iOS中我们需要这么写
viewB.backgroundColor = [UIColor red];
for (UIView *view in viewB.subviews) {
[view removeFromSuperview];
}
UIView *viewC3 = [[UIView alloc] initWithFrame:CGRectMake(100,80,120,40)];
[viewB addSubview:viewC3];
复制代码
在Flutter声明式UI中我们只需要这样
return ViewB(
color: red,
child: ViewC(...),
)
复制代码
声明式 UI 相对来说减轻了开发者的负担,不需要考虑如何调用 UI 实例的方法来改变不同的状态,只需要开发者描述当前的 UI 状态 (即各属性的值),框架会自动将 UI 从之前的状态切换到开发者描述的当前状态。
2、Flutter渲染三棵树
在Flutter中我们几乎所有的代码都以Widget方式呈现,在官方文档中对Widget描述如下:
/// Describes the configuration for an [Element].
///
/// Widgets are the central class hierarchy in the Flutter framework. A widget
/// is an immutable description of part of a user interface. Widgets can be
/// inflated into elements, which manage the underlying render tree.
复制代码
主要包含以下几个信息
Widget是指一部分 UI 的描述
Widget是不可变的
Widget是对Element配置的描述,而Element管理着底层的渲染树
第一个信息显而易见,各个Widget会根据我们所写的属性 (在 Flutter 里指状态 State) 展示在屏幕上。那么第二个信息,如果Widget是不可变的,而且用的是声明式 UI,那么随着用户的操作或者数据的变更,UI 是怎么更新的,Element 与 render tree 又是什么
Flutter 渲染树简介
在Flutter中,除了Widget树,还有Element树和Render树,这三棵树各司其职,完成了Flutter的渲染更新。其主要功能如下:
Widget是Element的配置描述,持有公共属性和提供公开方法
Element是Widget在树中特定位置的一个实例,这个是真正的节点,用来关联Widget与渲染对象。每一个Widget都对应着一个Element,但是Widget实例会经常变动,但是渲染树不能经常改变,因此Element会尽可能改变而不是重新创建
Render树中的对象,主管渲染,测量自身Size并进行绘制放置子节点
页面创建过程
我们编写Flutter应用入口函数都是 main 方法,其内部调用了 runApp 方法,所以我们直接看 runApp方法的实现
void runApp(Widget app) {
WidgetsFlutterBinding.ensureInitialized()
..scheduleAttachRootWidget(app)
..scheduleWarmUpFrame();
}
复制代码
整个函数使用连级符调用了三个方法:
WidgetsFlutterBinding 初始化(ensureInitialized)
根节点核心三棵树绑定穿件工作(scheduleAttachRootWidget)
绘制热身帧
我们主要查看前两个函数
WidgetsFlutterBinding.ensureInitialized() 函数
class WidgetsFlutterBinding extends BindingBase with GestureBinding, SchedulerBinding, ServicesBinding, PaintingBinding, SemanticsBinding, RendererBinding, WidgetsBinding {
static WidgetsBinding ensureInitialized() {
if (WidgetsBinding.instance == null)
WidgetsFlutterBinding();
return WidgetsBinding.instance!;
}
}
复制代码
WidgetsFlutterBinding 继承自 BindingBase,并且 with 了大量的 mixin 类。可能有人还不熟悉 mixin 语法,这里简单介绍一下:mixins 的中文意思是混入,就是在类中混入其他功能。在Dart中可以使用mixins实现类似多继承的功能。
通过 ensureInitialized() 方法我们可以得到一个全局单例的 WidgetsFlutterBinding 实例,且 mixin 的一堆 xxxBinding 也被实例化。 BindingBase 抽象类的构造方法中会调用 initInstances()方法,而各种 mixin 的 xxxBinding 实例化重点也都在各自的initInstances()方法中,每个 xxxBinding 的职责不同,具体如下:
https://zhuanlan.zhihu.com/p/...
https://zhuanlan.zhihu.com/p/...
https://zhuanlan.zhihu.com/p/...
https://zhuanlan.zhihu.com/p/...
https://zhuanlan.zhihu.com/p/...
https://zhuanlan.zhihu.com/p/...
https://zhuanlan.zhihu.com/p/...
https://zhuanlan.zhihu.com/p/...
https://zhuanlan.zhihu.com/p/...
WidgetsFlutterBinding:核心桥梁主体,Flutter app 全局唯一。
BindingBase:绑定服务抽象类。
GestureBinding:Flutter 手势事件绑定,处理屏幕事件分发及事件回调处理,其初始化方法中重点就是把事件处理回调_handlePointerDataPacket函数赋值给 window 的属性,以便 window 收到屏幕事件后调用,window 实例是 Framework 层与 Engine 层处理屏幕事件的桥梁。
SchedulerBinding:Flutter 绘制调度器相关绑定类,debug 编译模式时统计绘制流程时长等操作。
ServicesBinding:Flutter 系统平台消息监听绑定类。即 Platform 与 Flutter 层通信相关服务,同时注册监听了应用的生命周期回调。
PaintingBinding:Flutter 绘制预热缓存等绑定类。
SemanticsBinding:语义树和 Flutter 引擎之间的粘合剂绑定类。
RendererBinding:渲染树和 Flutter 引擎之间的粘合剂绑定类,内部重点是持有了渲染树的根节点。
WidgetsBinding:Widget 树和 Flutter 引擎之间的粘合剂绑定类。主要作用就是调度帧渲染任务,当然也可以运行非渲染任务。主要是瞬间渲染、持久渲染与渲染回调任务等,例如持久的帧渲染监听注册
我们主要看下渲染相关的 Binding
mixin WidgetsBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureBinding, RendererBinding, SemanticsBinding {
@override
void initInstances() {
super.initInstances();
_instance = this;
......
/**
*创建一个管理Element的类对象
*BuildOwner类用来跟踪哪些Element需要重建,并处理用于Element树的其他任务,
例如管理不活跃的Element等,调试模式触发重建等。
*/
_buildOwner = BuildOwner();
// 回调方法赋值,当第一个可构建元素被标记为脏时调用。
buildOwner!.onBuildScheduled = _handleBuildScheduled;
......
}
}
mixin RendererBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureBinding, SemanticsBinding, HitTestable {
@override
void initInstances() {
super.initInstances();
_instance = this;
/**
* 创建管理rendering渲染管道的类
* 提供接口调用用来触发渲染。
*/
_pipelineOwner = PipelineOwner(
onNeedVisualUpdate: ensureVisualUpdate,
onSemanticsOwnerCreated: _handleSemanticsOwnerCreated,
onSemanticsOwnerDisposed: _handleSemanticsOwnerDisposed,
);
// 一堆window变化相关的回调监听
window
..onMetricsChanged = handleMetricsChanged
..onTextScaleFactorChanged = handleTextScaleFactorChanged
..onPlatformBrightnessChanged = handlePlatformBrightnessChanged
..onSemanticsEnabledChanged = _handleSemanticsEnabledChanged
..onSemanticsAction = _handleSemanticsAction;
// 添加页面刷新回调函数
addPersistentFrameCallback(_handlePersistentFrameCallback);
// 创建RenderView对象,也就是RenderObject渲染树的根节点
initRenderView();
......
}
void initRenderView() {
......
// 渲染树的根节点对象
renderView = RenderView(configuration: createViewConfiguration(), window: window);
// renderView 添加到 _nodesNeedingLayout 和 _nodesNeedingPaint 列表
renderView.prepareInitialFrame();
}
RenderView get renderView => _pipelineOwner.rootNode! as RenderView;
set renderView(RenderView value) {
assert(value != null);
_pipelineOwner.rootNode = value;
}
}
复制代码
初始化过程我们已经得到了一些信息,注意两点:
RendererBinding中的RenderView就是RenderObject渲染树的根节点,代码中所有生成的 RenderObject 都会挂在到它的下面
WidgetsBinding内部的BuildOwner类是管理Element的类对象,用来跟踪哪些Widget需要重建
PipelineOwner是管理RenderObject类,提供接口调用用来触发渲染
接下来看初始化之后调用的函数scheduleAttachRootWidget
mixin WidgetsBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureBinding, RendererBinding, SemanticsBinding {
@protected
void scheduleAttachRootWidget(Widget rootWidget) {
// 一个耗时操作,异步运行。runApp会优先调用 scheduleWarmUpFrame() 渲染预热帧
Timer.run(() {
attachRootWidget(rootWidget);
});
}
void attachRootWidget(Widget rootWidget) {
// 检测是否是第一帧
final bool isBootstrapFrame = renderViewElement == null;
_readyToProduceFrames = true;
// 桥梁创建RenderObject、Element、Widget关系树,
// _renderViewElement值为attachToRenderTree方法返回值
// 继承自RenderObjectToWidgetElement,把他看成是一个普通的RenderObjectElement即可
_renderViewElement = RenderObjectToWidgetAdapter(
// RenderObjectWithChildMixin类型,继承自RenderObject,RenderObject继承自AbstractNode。
// 来自初始化时候RendererBinding的_pipelineOwner.rootNode
// 一个Flutter App全局只有一个PipelineOwner实例。
container: renderView,
debugShortDescription: '[root]',
// 我们平时写的dart Widget app
child: rootWidget,
// attach过程,buildOwner来自WidgetsBinding初始化时实例化的BuildOwner实例,
// renderViewElement值就是_renderViewElement自己,
// 此时由于调用完appach才赋值,所以首次进来也是null
).attachToRenderTree(buildOwner!, renderViewElement as RenderObjectToWidgetElement?);
if (isBootstrapFrame) {
// 首帧主动更新一下,内部本质是调用
// SchedulerBinding的scheduleFrame()方法。
// 进而本质调用了window.scheduleFrame()方法。
SchedulerBinding.instance!.ensureVisualUpdate();
}
}
}
复制代码
内部主要调用了RenderObjectToWidgetAdapter 类,进入类的内部
createElement 方法:每个widget都提供createElement方法,每个Element初始化时都必须有个widget参数,调用createElement时会把调用者传入
class RenderObjectToWidgetAdapter
......
// 我们编写dart的runApp函数参数中传递的Flutter应用Widget树根,上一步传进来的
final Widget? child;
// 继承自RenderObject,来自PipelineOwner对象的rootNode属性,
// 一个Flutter App全局只有一个PipelineOwner实例
// 上一步传进来的
final RenderObjectWithChildMixin
......
// 重写Widget的createElement实现,构建了一个RenderObjectToWidgetElement实例,它继承于Element。
// Element树的根结点是RenderObjectToWidgetElement。
@override
RenderObjectToWidgetElement
// 重写Widget的createRenderObject实现,container本质是一个RenderView。
// RenderObject树的根结点是RenderView。
@override
RenderObjectWithChildMixin
@override
void updateRenderObject(BuildContext context, RenderObject renderObject) { }
/**
- 上面代码片段中RenderObjectToWidgetAdapter实例创建后调用
- owner来自WidgetsBinding初始化时实例化的BuildOwner实例,element 值就是自己。
- 该方法创建根Element(RenderObjectToWidgetElement),
- 并将Element与Widget进行关联,即创建WidgetTree对应的ElementTree。
- 如果Element已经创建过则将根Element中关联的Widget设为新的(即_newWidget)。
- 可以看见Element只会创建一次,后面都是直接复用的。
*/
// 由于首次实例化RenderObjectToWidgetAdapter调用attachToRenderTree后才不为null,所以当前流程为null
if (element == null) {
// 在lockState里面代码执行过程中禁止调用setState方法
owner.lockState(() {
// 创建一个Element实例
// 构建一个RenderObjectToWidgetElement实例,
// 继承RootRenderObjectElement,又继续继承RenderObjectElement,接着继承Element。
element = createElement();
assert(element != null);
// 给根Element的owner属性赋值为WidgetsBinding初始化时实例化的BuildOwner实例。
element!.assignOwner(owner);
});
// 重点!mount里面RenderObject
owner.buildScope(element!, () {
element!.mount(null, null);
});
} else {
// 更新widget树时_newWidget赋值为新的,然后element数根标记为markNeedsBuild
element._newWidget = this;
element.markNeedsBuild();
}
return element!;
}
......
}
复制代码
在创建页面时候BuildOwner.buildScope 方法可以简单的看成只执行了 block 方法,主要看 element 的 mount 方法
class RenderObjectToWidgetElement
......
@override
void mount(Element? parent, Object? newSlot) {
super.mount(parent, newSlot);
_rebuild();
}
...
}
复制代码
基类Element的mount方法,仅仅是把parent记录在此element中,更新slot和depth信息
abstract class Element extends DiagnosticableTree implements BuildContext {
void mount(Element? parent, Object? newSlot) {
......
// 查找父 element ,获取父element对自己的布局约束等
_parent = parent;
_slot = newSlot;
_lifecycleState = _ElementLifecycle.active;
_depth = _parent != null ? _parent!.depth + 1 : 1;
if (parent != null) {
_owner = parent.owner;
}
final Key? key = widget.key;
if (key is GlobalKey) {
owner!._registerGlobalKey(key, this);
}
_updateInheritance();
}
}
复制代码
再看 _rebuild 方法
class RenderObjectToWidgetElement
......
void _rebuild() {
try {
// updateChild同样也是界面创建与刷新时的重要处理过程,后面会详细说明,
// 这里只需要认为这里会进行子控件的添加,而且是递归添加处理,分别调用子控件的mount操作。
// 其中widget.child就是我们传入的Widget实例
_child = updateChild(_child, widget.child, _rootChildSlot);
} catch (exception, stack) {
final FlutterErrorDetails details = FlutterErrorDetails(
exception: exception,
stack: stack,
library: 'widgets library',
context: ErrorDescription('attaching to the render tree'),
);
FlutterError.reportError(details);
final Widget error = ErrorWidget.builder(details);
_child = updateChild(null, error, _rootChildSlot);
}
}
...
}
复制代码
目前加载过程流程图如下:
创建时 updateChild 的操作
abstract class Element extends DiagnosticableTree implements BuildContext {
Element? updateChild(Element? child, Widget? newWidget, Object? newSlot) {
......
final Element newChild;
// 第一次,Element child是null,执行else里的逻辑,
// inflateWidget使用子widget来创建一个子Element
if (child != null) {
......
} else {
newChild = inflateWidget(newWidget, newSlot);
}
......
return newChild;
}
Element inflateWidget(Widget newWidget, Object? newSlot) {
assert(newWidget != null);
final Key? key = newWidget.key;
// 如果 widget的Key是GlobalKey的话,会先从GlobalKey中获取引用的Element,
// 如果有lement的话就更新复用
if (key is GlobalKey) {
final Element? newChild = _retakeInactiveElement(key, newWidget);
if (newChild != null) {
assert(newChild._parent == null);
assert(() {
_debugCheckForCycles(newChild);
return true;
}());
newChild._activateWithParent(this, newSlot);
final Element? updatedChild = updateChild(newChild, newWidget, newSlot);
assert(newChild == updatedChild);
return updatedChild!;
}
}
// 否则就用 widget调用其createElement()来创建了一个element
// Element初始化需要Widget参数,创建完成后newChild的widget参数就是newWidget
final Element newChild = newWidget.createElement();
assert(() {
_debugCheckForCycles(newChild);
return true;
}());
// 接着就调用新建的子element的mount方法
newChild.mount(this, newSlot);
assert(newChild._lifecycleState == _ElementLifecycle.active);
return newChild;
}
}
复制代码
mount方法比较复杂根据不同的 element 类型有几种分支, element 分为两种类型, RenderObjectElement 和 ComponentElement,具体类如下图
具体代码调用过程如下:ComponentElement类的mount方法
abstract class ComponentElement extends Element {
void mount(Element? parent, Object? newSlot) {
super.mount(parent, newSlot);
assert(_child == null);
assert(_lifecycleState == _ElementLifecycle.active);
_firstBuild();
assert(_child != null);
}
void _firstBuild() {
rebuild();
}
@override
void performRebuild() {
......
Widget? built;
try {
......
// build函数为子类StatefulElement和StatelessElement的build方法
built = build();
.....
} catch (e, stack) {
.....
} finally {
......
}
......
try {
_child = updateChild(_child, built, slot);
} catch (e, stack) {
......
}
......
}
}
复制代码
class StatelessElement extends ComponentElement {
......
@override
Widget build() => widget.build(this);
......
}
class StatefulElement extends ComponentElement {
......
@override
Widget build() => state.build(this);
......
}
复制代码
在 Flutter 里面最常见的 StatelessWidget 和 StatefulWidget 的 build 方法就是在这里被调用的。然后就又调用到了updateChild方法,这就回到了上边流程一直往下遍历创建widget树。
SingleChildRenderObjectElement类和MultiChildRenderObjectElement类都继承自RenderObjectElement先看下RenderObjectElement类的mount方法
abstract class RenderObjectElement extends Element {
@override
RenderObjectWidget get widget => super.widget as RenderObjectWidget;
@override
RenderObject get renderObject => _renderObject!;
RenderObject? _renderObject;
@override
void mount(Element? parent, Object? newSlot) {
super.mount(parent, newSlot);
......
// 通过widget树调用createRenderObject方法传入Element实例自己获取RenderObject渲染树。
_renderObject = widget.createRenderObject(this);
attachRenderObject(newSlot);
_dirty = false;
......
}
@override
void attachRenderObject(Object? newSlot) {
assert(_ancestorRenderObjectElement == null);
_slot = newSlot;
// 寻找可用的父RenderObject,再添加新的节点
_ancestorRenderObjectElement = _findAncestorRenderObjectElement();
_ancestorRenderObjectElement?.insertRenderObjectChild(renderObject, newSlot);
final ParentDataElement? parentDataElement = _findAncestorParentDataElement();
if (parentDataElement != null)
_updateParentData(parentDataElement.widget);
}
}
复制代码
SingleChildRenderObjectElement类和MultiChildRenderObjectElement类的mount方法
class SingleChildRenderObjectElement extends RenderObjectElement {
@override
void mount(Element? parent, Object? newSlot) {
super.mount(parent, newSlot);
_child = updateChild(_child, widget.child, null);
}
}
class MultiChildRenderObjectElement extends RenderObjectElement {
@override
void mount(Element? parent, Object? newSlot) {
super.mount(parent, newSlot);
final List children = List.filled(widget.children.length, _NullElement.instance, growable: false);
Element? previousChild;
for (int i = 0;
i < children.length;
i += 1) {
final Element newChild = inflateWidget(widget.children[i], IndexedSlot(i, previousChild));
children[i] = newChild;
previousChild = newChild;
}
_children = children;
}
}
复制代码
通过代码可以看到 Element 调用mount()方法时
componentElement的mount方法主要作用是执行build(根据类型区分widget.build, state.build)
renderObjectElement 的mount方法主要作用是生成RenderObject
Element创建完成时就会调用 mount, 调用顺序为 mount -> _firstBuild -> reBuild -> performRebuild -> build
总结一下:updateChild是一个递归的过程,总结下来有下面几个步骤
Element如果是RenderObjectElement则创建RenderObject,并从祖先找到上一个RenderObjectElement,然后调用祖先RenderObjectElement的RenderObject的insertRenderObjectChild方法插入创建的RenderObject
如果子widget需要build出来就调用build方法创建子widget,如果不需要直接在成员变量可以拿到子widget
调用子widget的createElement创建子Element
调用子Element的mount方法将子Element的parent设置成自己,然后子Element去到第1步
从页面的创建过程可以看出,Widget、Element 和 Render,这三棵树的对应关系大概可以表示为:
每个Widget都有一个Element,Widget从渲染的角度进行分类,分为可渲染Widget与不可渲染Widget,只有生成Element对应为RenderObjectElement和它的子类才有RenderObject可以渲染到页面上,我们常用的 StatelessWidget 与 StatefulWidget 就属于不可渲染的Widge,具体对应关系如下:
WidgetElementRenderObjectStatelessWidgetStatelessElement-StatefulWidgetStatefulElement-ProxyWidgetProxyElement-InheritedWidgetInheritedElement-SingleChildRenderObjectWidgetSingleChildRenderObjectElementRenderObjectMultiChildRenderObjectWidgetMultiChildRenderObjectElementRenderObjectRenderObjectWidgetRenderObjectElementRenderObject
构建流程如下:
scheduleAttachRootWidget 函数完成Flutter App 中的 Widget、Element 和 RenderObject树生成和相互关联。在函数最后调用了SchedulerBinding.instance!.ensureVisualUpdate(); 通知Engine有UI需要更新渲染页面 (后面详细描述)
总结一下runApp方法的大体过程
调用runApp(Widget)函数传入一个Widget作为根Widget。 Widget只是一个配置类,不是实际的UI元素。
runApp通过WidgetsFlutterBindingmixIn继承一众父类进行初始化。
其中,RendererBinding 中的 renderView对象,是实际的渲染对象。
通过RenderObjectToWidgetAdapter类(继承自 RenderObjectWidget我们 runApp 中传递的 Widget 树就被追加到了这个树根的 child 属性上)生成一个RenderObjectToWidgetElement
最后调用SchedulerBinding.instance.ensureVisualUpdate()函数,等待下一帧渲染
scheduleAttachRootWidget是一个耗时操作,异步运行。runApp会优先调用scheduleWarmUpFrame()渲染预热帧。
页面构建流程如下:
更新页面
在 Flutter 中我们直接通过 setState 方法来对页面进行刷新,所以直接查看源码,去掉了 assert 异常处理相关代码
abstract class State
@protected
void setState(VoidCallback fn) {
final Object? result = fn() as dynamic;
_element!.markNeedsBuild();
}
}
复制代码
直接调用 setState 传入的函数,然后调用 Element 的 markNeedsBuild 方法
abstract class Element extends DiagnosticableTree implements BuildContext {
void markNeedsBuild() {
if (_lifecycleState != _ElementLifecycle.active)
return;
.....if (dirty)
return;
_dirty = true;
owner!.scheduleBuildFor(this);
}
}
复制代码
这里面将 Element 标记为 dirty,然后调用 BuildOwner 类的 scheduleBuildFor 方法,BuildOwner 实例在 WidgetsBinding 中初始化整个App中只有一个实例 继续查看:
class BuildOwner {
void scheduleBuildFor(Element element) {
......
// 判断 element 是否已经加入到 _dirtyElements 列表中,
// 若是已经在列表中,就直接返回,不用再执行下面的操做
if (element._inDirtyList) {
_dirtyElementsNeedsResorting = true;
return;
}
// 判断 _scheduledFlushDirtyElements 是否为 false ,这个变量表示当前是否正在 rebuild
// _dirtyElements 中的元素。若是没有正在 rebuild ,而且 onBuildScheduled 回调不为空
// 就调用 onBuildScheduled 函数
if (!_scheduledFlushDirtyElements && onBuildScheduled != null) {
_scheduledFlushDirtyElements = true;
onBuildScheduled!();
}_dirtyElements.add(element);
element._inDirtyList = true;
......
}
}
复制代码
这里将该element加入到_dirtyElements中,标记这个节点刷新时需要进行处理。onBuildScheduled 方法在初始化中设置的具体代码为 WidgetsBinding 中 buildOwner!.onBuildScheduled = _handleBuildScheduled; 具体代码如下
mixin WidgetsBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureBinding, RendererBinding, SemanticsBinding {
void _handleBuildScheduled() {
ensureVisualUpdate();
}
}
复制代码
直接调用到SchedulerBinding类的ensureVisualUpdate方法
mixin SchedulerBinding on BindingBase {
void ensureVisualUpdate() {
switch (schedulerPhase) {
case SchedulerPhase.idle:
case SchedulerPhase.postFrameCallbacks:
scheduleFrame();
return;
case SchedulerPhase.transientCallbacks:
case SchedulerPhase.midFrameMicrotasks:
case SchedulerPhase.persistentCallbacks:
return;
}
}
void scheduleFrame() {
if (_hasScheduledFrame || !framesEnabled)
return;
ensureFrameCallbacksRegistered();
// 执行代码
// void scheduleFrame() => platformDispatcher.scheduleFrame();
// void scheduleFrame() native 'PlatformConfiguration_scheduleFrame';
// 调用 Flutter Engine 方法
window.scheduleFrame();
_hasScheduledFrame = true;
}
@protected
void ensureFrameCallbacksRegistered() {
// 调用 void scheduleFrame() native 'PlatformConfiguration_scheduleFrame'
// 在下一个适当的机会调用 onBeginFrame 和 onDrawFrame 回调 ()
// onBeginFrame 主要进行是做一些准备工作,让framework准备好绘制工作,例如重新设置状态、变量等等
window.onBeginFrame ??= _handleBeginFrame;
window.onDrawFrame ??= _handleDrawFrame;
}
void _handleDrawFrame() {
......
handleDrawFrame();
}
void handleDrawFrame() {
assert(_schedulerPhase == SchedulerPhase.midFrameMicrotasks);
Timeline.finishSync();
// end the "Animate" phase
try {
// 执行 _persistentCallbacks 数组内的 callback
// _persistentCallbacks 数组在初始化 WidgetsBinding 中 addPersistentFrameCallback 方法插入
_schedulerPhase = SchedulerPhase.persistentCallbacks;
for (final FrameCallback callback in _persistentCallbacks)
_invokeFrameCallback(callback, _currentFrameTimeStamp!);
......
} finally {
......
_currentFrameTimeStamp = null;
}
}
}
复制代码
经过一系列的函数调用,调用 SchedulerBinding.instance.ensureVisualUpdate 最后会调用到WidgetsBinding 中 addPersistentFrameCallback 设置的方法
mixin RendererBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureBinding, SemanticsBinding, HitTestable {
void _handlePersistentFrameCallback(Duration timeStamp) {
drawFrame();
_scheduleMouseTrackerUpdate();
}
}
复制代码
drawFrame函数有两个,一个在 WidgetsBinding 内一个在
RendererBinding内,优先调用WidgetsBinding内的函数
@override
mixin WidgetsBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureBinding, RendererBinding, SemanticsBinding {
void drawFrame() {
......
try {
if (renderViewElement != null)
buildOwner!.buildScope(renderViewElement!);
super.drawFrame();
// 清理不再使用的 element
buildOwner!.finalizeTree();
} finally {
......
}
......
}
}
复制代码
又调用到了buildOwner.buildScope方法,之前创建界面时调用了这个方法,现在刷新时也用到了,创建页面时只是简单的看成只执行了 block 方法,现在详细说明一下:
void buildScope(Element context, [ VoidCallback? callback ]) {
if (callback == null && _dirtyElements.isEmpty)
return;
......
Timeline.startSync('Build', arguments: timelineArgumentsIndicatingLandmarkEvent);
try {
_scheduledFlushDirtyElements = true;
if (callback != null) {
......
_dirtyElementsNeedsResorting = false;
try {
callback();
} finally {
......
}
}
// 首先将_dirtyElements进行排序,这是因为节点可能有很多个,
// 如果其中两个节点存在级联关系,父级的Widget build操作必然会调用到子级的Widget build,
// 如果子级又自己build一次,相当于出现了重复操作。因此通过深度排序就会避免这个问题
_dirtyElements.sort(Element._sort);
_dirtyElementsNeedsResorting = false;
int dirtyCount = _dirtyElements.length;
int index = 0;
// 对每一个Element进行遍历
while (index < dirtyCount) {
......
try {
// 执行rebuild操作
_dirtyElements[index].rebuild();
} catch (e, stack) {
......
}
index += 1;
// 如果在遍历过程中增加了新的节点,那么就需要重新排序
if (dirtyCount < _dirtyElements.length || _dirtyElementsNeedsResorting!) {
_dirtyElements.sort(Element._sort);
_dirtyElementsNeedsResorting = false;
dirtyCount = _dirtyElements.length;
while (index > 0 && _dirtyElements[index - 1].dirty) {
index -= 1;
}
}
}
......
return true;
}());
} finally {
// 所有Element都rebuild后,清空 _dirtyElements 集合,节点状态恢复正常
for (final Element element in _dirtyElements) {
assert(element._inDirtyList);
element._inDirtyList = false;
}
_dirtyElements.clear();
_scheduledFlushDirtyElements = false;
_dirtyElementsNeedsResorting = null;
Timeline.finishSync();
......
}
assert(_debugStateLockLevel >= 0);
}
复制代码
Element.rebuild()方法调用了子类的 performRebuild()方法ComponentElement类页面创建过程看到过,在更新时会再次调用
@override
void performRebuild() {
......
Widget? built;
try {
......
// build函数为子类StatefulElement和StatelessElement的build方法
built = build();
.....
} catch (e, stack) {
.....
} finally {
......
_dirty = false;
}
......
try {
_child = updateChild(_child, built, slot);
} catch (e, stack) {
......
}
......
}
复制代码
最后还是回到 updateChild 方法,构建时只看了 child 为空的情况,现在看 child 不为空只更新的情况
abstract class Element extends DiagnosticableTree implements BuildContext {
Element? updateChild(Element? child, Widget? newWidget, Object? newSlot) {
// 如果不存在新的Widget,那么说明这一个节点应该取消掉了,
// 执行deactivateChild 删除节点方法。
if (newWidget == null) {
if (child != null)
deactivateChild(child);
return null;
}final Element newChild;
if (child != null) {
bool hasSameSuperclass = true;
if (hasSameSuperclass && child.widget == newWidget) {
// 如果子节点的widget和新的widget一致(这里的一致指的是同一个对象)
// 直接返回这个子节点。
if (child.slot != newSlot)
updateSlotForChild(child, newSlot);
newChild = child;
} else if (hasSameSuperclass && Widget.canUpdate(child.widget, newWidget)) {
// 如果两个widget不是同一个对象,判断类型是否相同,通过canUpdate方法判断
// 依据是Widget类型一致,同时Key一致
// 这种情况下,只需要更新子节点
// 因此这一步就是widget变更,但是element不变更
if (child.slot != newSlot)
updateSlotForChild(child, newSlot);
child.update(newWidget);
assert(child.widget == newWidget);
assert(() {
child.owner!._debugElementWasRebuilt(child);
return true;
}());
newChild = child;
} else {
// 其它情况下则认为子节点是新增的,先删除原来的再建新的
// 调用`inflateWidget`进行子节点创建
// 里面与创建界面相同,执行了mount操作
deactivateChild(child);
assert(child._parent == null);
newChild = inflateWidget(newWidget, newSlot);
}
} else {
......
}
......
return newChild;
}
}
复制代码
element.update方法会把newWidget记录下来
abstract class Element extends DiagnosticableTree implements BuildContext {
@mustCallSuper
void update(covariant Widget newWidget) {
_widget = newWidget;
}
}
复制代码
StatelessElement.update方法会调用rebuild,rebuild中会调用performRebuild()去重建其子widget,类似一个递归的流程
class StatelessElement extends ComponentElement {
@override
void update(StatelessWidget newWidget) {
super.update(newWidget);
_dirty = true;
rebuild();
}
}
复制代码
StatefulElement.update方法,先回调state.didUpdateWidget(这里就是我们在自定义Widget写的生命周期回调函数就是在这里触发的),最后又调用rebuild
@override
class StatefulElement extends ComponentElement {
void update(StatefulWidget newWidget) {
super.update(newWidget);
final StatefulWidget oldWidget = state._widget!;
_dirty = true;
state._widget = widget as StatefulWidget;
try {
final Object? debugCheckForReturnedFuture = state.didUpdateWidget(oldWidget) as dynamic;
} finally {
}
rebuild();
}
}
复制代码
SingleChildRenderObjectElement.update方法,调用 updateChild
class SingleChildRenderObjectElement extends RenderObjectElement {
@override
void update(SingleChildRenderObjectWidget newWidget) {
super.update(newWidget);
assert(widget == newWidget);
_child = updateChild(_child, widget.child, null);
}
}
复制代码
MultiChildRenderObjectElement.update方法,调用 updateChildren,内部循环调用updateChild方法
class MultiChildRenderObjectElement extends RenderObjectElement {
@override
void update(MultiChildRenderObjectWidget newWidget) {
super.update(newWidget);
assert(widget == newWidget);
_children = updateChildren(_children, widget.children, forgottenChildren: _forgottenChildren);
_forgottenChildren.clear();
}
}
复制代码
RenderObjectElement.update方法,update方法里面只是更新widget的配置,这里会对 renderObject进行修改
abstract class RenderObjectElement extends Element {
@override
void update(covariant RenderObjectWidget newWidget) {
super.update(newWidget);
widget.updateRenderObject(this, renderObject);
_dirty = false;
【Flutter 页面构建渲染更新流程解析】}
}
复制代码
RenderObjectElement 的 performRebuild方法
abstract class RenderObjectElement extends Element {
@override
void performRebuild() {
widget.updateRenderObject(this, renderObject);
_dirty = false;
}
}
复制代码
widget.updateRenderObject每一种RenderObjectElement都会有自己的updateRenderObject处理方式,处理完成后如果需要重新计算大小宽高就会加到 PipelineOwner 的 _nodesNeedingLayout列表中,如果需要重新绘制就加到 PipelineOwner 的 _nodesNeedingPaint 列表中
剩下执行super.drawFrame() 这行代码就是调用 RendererBinding 类的 drawFrame 函数
mixin RendererBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureBinding, SemanticsBinding, HitTestable {
@protected
void drawFrame() {
assert(renderView != null);
// 调用 RenderView.performLayout(),遍历子节点,子节点在widget.updateRenderObject已经加入到列表内
// 调用每个节点的 layout(),RenderObject的排版数据,使得每个RenderObject最终都能有正确的大小和位置
pipelineOwner.flushLayout();
// 更新渲染对象,此阶段每个渲染对象都会了解其子项是否需要合成
// 在绘制阶段使用此信息选择如何实现裁剪等视觉效果
pipelineOwner.flushCompositingBits();
// 会调用 RenderView.paint() 最终触发各个节点的 paint(),最终生成一棵Layer Tree,并把绘制指令保存在Layer中
pipelineOwner.flushPaint();
if (sendFramesToEngine) {
// 把Layer Tree提交给GPU
renderView.compositeFrame();
//
pipelineOwner.flushSemantics();
// this also sends the semantics to the OS.
_firstFrameSent = true;
}
}
}
复制代码
总结一下 setState 过程
首先调用 markNeedsBuild 方法,将 element 的 dirty 标记为 true,表示需要重建
接着调用 scheduleBuildFor ,将当前的 element 添加到 _dirtyElements 列表中
调用buildOwner.buildScope,函数内部对 _dirtyElements 列表中的 element 调用 rebuild 函数
rebuild 函数调用 updateChild 循环更新子 element
RenderObjectElement 调用 updateRenderObject ,对 RenderObject 更新
最后调用 pipelineOwner相关方法最后更新界面