前言
在flutter中通过Widget构建想要的UI,但它并非真正的UI组件,真正的UI组件是Element,那Widget和Element是如何关联起来的呢?内部的构建过程和机制又是怎样的呢?本文将带着这里两个问题从源码角度进行分析。先从一段代码看起:
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
void main(){
runApp(StudyApp());
}
class StudyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home:HomePage(),
);
}
}
class HomePage extends StatefulWidget {
@override
State createState() => _HomePageState();
}
class _HomePageState extends State {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar:AppBar(title:Text("学习")),
body:Column(crossAxisAlignment:CrossAxisAlignment.center,children: [
Text("这里是一段文字"),
RaisedButton()
]),
);
}
}
这段代码运行后默认将在屏幕上显示一个导航栏,一个文本和一个按钮。上面中HomePage继承于StatefulWidget,StatefulWidget又继承于Widget,事实上flutter中就是通过Widget的各种子类来构建我们想要的UI。那Widget就是构建UI的组件吗?答案并不是。
关于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组件是Element。Widget作为Element的配置属性被单独抽离成了一个类,这就是flutter中UI组件的一个特点
Widget首次渲染流程
1、runApp()源码
void runApp(Widget app) {
WidgetsFlutterBinding.ensureInitialized()
..scheduleAttachRootWidget(app)
..scheduleWarmUpFrame();
}
该函数的作用为将app作为根widget加载到屏幕上。这里采用链式调用的方式来完成,点开WidgetsFlutterBinding.ensureInitialized()可以看到,他是一个单例。接下来进入scheduleAttachRootWidget()函数
2、scheduleAttachRootWidget()源码
void scheduleAttachRootWidget(Widget rootWidget) {
Timer.run(() {
attachRootWidget(rootWidget);
});
}
scheduleAttachRootWidget并没有直接调用attachRootWidget(),而是通过Timer将其作为一个event task添加到主Root isolate事件循环中,接下来进入attachRootWidget()函数
3、attachRootWidget()源码
void attachRootWidget(Widget rootWidget) {
_readyToProduceFrames = true;
_renderViewElement = RenderObjectToWidgetAdapter(
container: renderView,
debugShortDescription: '[root]',
child: rootWidget,
).attachToRenderTree(buildOwner, renderViewElement as RenderObjectToWidgetElement);
}
这里有两个变量,renderView和buildOwner,它们分别代表什么呢?renderView代表了渲染树上的根Element对象,它是在RenderBinding类的initInstances()中初始化,buildOwner代表了整个渲染管线对象,用于驱动整个渲染,它是在RenderBinding对象中生成的
4、attachToRenderTree()源码
RenderObjectToWidgetElement attachToRenderTree(BuildOwner owner, [ RenderObjectToWidgetElement element ]) {
if (element == null) {
owner.lockState(() {
element = createElement();
assert(element != null);
element.assignOwner(owner);
});
owner.buildScope(element, () {
element.mount(null, null);
});
// This is most likely the first time the framework is ready to produce
// a frame. Ensure that we are asked for one.
SchedulerBinding.instance.ensureVisualUpdate();
} else {
element._newWidget = this;
element.markNeedsBuild();
}
return element;
}
这里分两步,第一步调用createElement()创建根Element,即RenderObjectToWidgetElement
第二步调用这个根Element的mount函数进行挂载。然后看一下RenderObjectToWidgetElement类的mount函数实现
@override
void mount(Element parent, dynamic newSlot) {
assert(parent == null);
super.mount(parent, newSlot);
_rebuild();
}
接着继续看父类RootRenderObjectElement的mount()函数,撒也没做。然后再进入父类RenderObjectElement的mount()函数
@override
void mount(Element parent, dynamic newSlot) {
super.mount(parent, newSlot);
assert(() {
_debugDoingBuild = true;
return true;
}());
_renderObject = widget.createRenderObject(this);
assert(() {
_debugDoingBuild = false;
return true;
}());
assert(() {
_debugUpdateRenderObjectOwner();
return true;
}());
assert(_slot == newSlot);
attachRenderObject(newSlot);
_dirty = false;
}
上面_renderObject = widget.createRenderObject(this);就创建了渲染对象,这个渲染对象就作为渲染树的根对象。
Element各个子类的作用:
ComponentElement
组件Element,衍生出StatelessElement和StatefulElement两个具体的组件Element,专门用于将各个基础的Widget(例如Text,RaiseButton等)通过build函数组合到一起。RenderObjectElement
渲染Element,拥有具体的RenderObject,通过createRenderObject()函数指定RootRenderObjectElement
作为渲染树中所有渲染对象的根渲染对象
接下来进入RenderObjectToWidgetElement的_rebuild()函数
void _rebuild() {
try {
_child = updateChild(_child, widget.child, _rootChildSlot);
assert(_child != null);
} 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);
}
}
_child = updateChild(_child, widget.child, _rootChildSlot);通过updateChild()函数去解析Widget树中层次结构中的对象,进入Element的updateChild()函数
Element updateChild(Element child, Widget newWidget, dynamic newSlot) {
if (newWidget == null) {
if (child != null)
deactivateChild(child);
return null;
}
Element newChild;
if (child != null) {
bool hasSameSuperclass = true;
if (hasSameSuperclass && child.widget == newWidget) {
if (child.slot != newSlot)
updateSlotForChild(child, newSlot);
newChild = child;
} else if (hasSameSuperclass && Widget.canUpdate(child.widget, newWidget)) { //关键代码1
if (child.slot != newSlot)
updateSlotForChild(child, newSlot);
child.update(newWidget);
assert(child.widget == newWidget);
assert(() {
child.owner._debugElementWasRebuilt(child);
return true;
}());
newChild = child;
} else { //关键代码2
deactivateChild(child);
assert(child._parent == null);
newChild = inflateWidget(newWidget, newSlot);
}
} else { //关键代码3
newChild = inflateWidget(newWidget, newSlot);
}
assert(() {
if (child != null)
_debugRemoveGlobalKeyReservation(child);
final Key key = newWidget?.key;
if (key is GlobalKey) {
key._debugReserveFor(this, newChild);
}
return true;
}());
return newChild;
}
这里解析关键代码,参数child就代表当前Widget对应的Element(对于首次加载app,他指的是前面RenderObjectToWidgetElement对象实例),参数newWidget代表当前Widget的第一个子Widget(即build函数返回的那个Widget),现在看下关键代码1
通过Widget.canUpdate(child.widget, newWidget)比对当前widget和新配置对象Widget是否一样,一样则直接调用Element的update()函数,不一样则进入关键代码2
先调用deactivateChild(child);将当前Element标记为非激活Element,然后调用newChild = inflateWidget(newWidget, newSlot);重新构建当前对象的Element
接着进入inflateWidget()函数
Element inflateWidget(Widget newWidget, dynamic newSlot) {
assert(newWidget != null);
final Key key = newWidget.key;
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;
}
}
final Element newChild = newWidget.createElement();
assert(() {
_debugCheckForCycles(newChild);
return true;
}());
newChild.mount(this, newSlot);
assert(newChild._debugLifecycleState == _ElementLifecycle.active);
return newChild;
}
先调用final Element newChild = newWidget.createElement();创建对应的Element,然后调用它的mount()函数进行挂载,可以看到又进入到了和前面一样的流程中了
flutter就是这样通过嵌套调用的方式将Widgets树构建成Element树,那flutter是如何构建渲染树的呢?前面我们知道,Element的子类有两种CommponentElment和RenderObjectElment,前者一般用于我们构建Widgets树,它不直接拥有渲染树中的渲染对象,后者则直接拥有渲染树中的渲染对象。
当通过inflateWidget()函数不停的嵌套构建Element时,如果构建的Element为CommponentElment那么持续createElement()-->mount()-->rebuild()-->performRebuild()->updateChild()->inflateWidget()....
如果构建的Element为RenderObjectElment那么持续createElement()-->mount()->createRenderObject()->attachRenderObject() 将Element对应的渲染对象加入渲染树中
以上就是Widget树-->Element树-->渲染树的构建流程以及原理,对于我们来说,大部分情况下都是通过Widget来构建我们自己的UI界面,渲染树相关对象以及Element树中的相关对象的关联过程flutter已经帮我们封装好了。
关于RenderObjectWidget
我们都知道StatelessWidget用于构建无状态更新的Widget,StatefulWidget用于构建有状态更新的Widget,他们对应的Element是ComponentElement的子类。RenderObjectWidget则代表着具体的渲染对象的Widget,它对应的Element为RenderObjectElement,它有两个具体的子类:
- MultiChildRenderObjectElement 代表着组合组件Element
- SingleChildRenderObjectElement 代表着单个的Element
只有RenderObjectElement才拥有一个RenderObject对象用于构建渲染树中的渲染对象