众所周知,Flutter中的UI都是通过Widget来呈现给用户看的。Android中的UI我们都很了解,主要是通过View和ViewGroup来呈现给用户看的。Android view或ViewGroup的属性,子View的添加删除,生命周期管理,测量,布局,绘制等操作都是由View或ViewGroup本身来管理的。然而Flutter中属性,配置等信息是由Widget来管理的。Widget的增加,删除操作是由Element类来管理的。测量,布局,绘制等操作是由RenderObject对象来管理的。本文让我来探究Flutter界面更新操作的原理。
从main()方法说起
void main() => runApp(MyApp());
//mixin WidgetsBinding
void runApp(Widget app) {
WidgetsFlutterBinding.ensureInitialized()
..scheduleAttachRootWidget(app)
..scheduleWarmUpFrame();
}
//WidgetsBinding
@protected
void scheduleAttachRootWidget(Widget rootWidget) {
Timer.run(() {
attachRootWidget(rootWidget);
});
}
//WidgetsBinding
void attachRootWidget(Widget rootWidget) {
_renderViewElement = RenderObjectToWidgetAdapter<RenderBox>(
container: renderView,
debugShortDescription: '[root]',
child: rootWidget,
).attachToRenderTree(buildOwner, renderViewElement);
}
在attachRootWidget(Widget rootWidget)方法中。我们创建了RenderObjectToWidgetAdapter对象,并且把MyApp对象赋值给它的child对象。我们来看下RenderObjectToWidgetAdapter。首先我们看到下RenderObjectToWidgetAdapter是个Widget的子类。它是整个Flutter项目的根Widget,对标Android的DecorView。
class RenderObjectToWidgetAdapter<T extends RenderObject> extends RenderObjectWidget {
RenderObjectToWidgetAdapter({
this.child,
this.container,
this.debugShortDescription,
}) : super(key: GlobalObjectKey(container));
/// The widget below this widget in the tree.
///
/// {@macro flutter.widgets.child}
final Widget child;
/// The [RenderObject] that is the parent of the [Element] created by this widget.
final RenderObjectWithChildMixin<T> container;
/// A short description of this widget used by debugging aids.
final String debugShortDescription;
@override
RenderObjectToWidgetElement<T> createElement() => RenderObjectToWidgetElement<T>(this);
@override
RenderObjectWithChildMixin<T> createRenderObject(BuildContext context) => container;
@override
void updateRenderObject(BuildContext context, RenderObject renderObject) { }
/// Inflate this widget and actually set the resulting [RenderObject] as the
/// child of [container].
///
/// If `element` is null, this function will create a new element. Otherwise,
/// the given element will have an update scheduled to switch to this widget.
///
/// Used by [runApp] to bootstrap applications.
RenderObjectToWidgetElement<T> attachToRenderTree(BuildOwner owner, [ RenderObjectToWidgetElement<T> element ]) {
if (element == null) {
owner.lockState(() {
element = createElement();//创建RenderObjectToWidgetElement()
assert(element != null);
element.assignOwner(owner);
});
owner.buildScope(element, () {
element.mount(null, null);//_rebuild自己,rebuild dirty Elements
});
// 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;
}
@override
String toStringShort() => debugShortDescription ?? super.toStringShort();
}
创建完调用RenderObjectToWidgetAdapter之后,调用它的attachToRenderTree方法。该方法的主要功能是
- 如果Element为空,创建新的Element
- 将Element与Widget关联起来
- 调用element的mount方法,将Element加入到renderTree中
RenderObjectToWidgetElement<T> attachToRenderTree(BuildOwner owner, [ RenderObjectToWidgetElement<T> element ]) {
if (element == null) {
owner.lockState(() {
element = createElement();//创建RenderObjectToWidgetElement()
assert(element != null);
element.assignOwner(owner);
});
owner.buildScope(element, () {
element.mount(null, null);//_rebuild自己,rebuild dirty Elements
});
// 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;
}
owner.buildScope(),首先调用的是callback(),也就是element.mount(null,null)。然后会遍历脏的Element并调用rebuild()方法
// BuildOwner
void buildScope(Element context, [ VoidCallback callback ]) {
if (callback == null && _dirtyElements.isEmpty)
return;
try {
_scheduledFlushDirtyElements = true;
if (callback != null) {
_dirtyElementsNeedsResorting = false;
try {
callback();
} finally {
}
}
_dirtyElements.sort(Element._sort);
_dirtyElementsNeedsResorting = false;
int dirtyCount = _dirtyElements.length;
int index = 0;
while (index < dirtyCount) {
try {
_dirtyElements[index].rebuild();
} catch (e, stack) {
_debugReportException(
ErrorDescription('while rebuilding dirty elements'),
e,
stack,
informationCollector: () sync* {
yield DiagnosticsDebugCreator(DebugCreator(_dirtyElements[index]));
yield _dirtyElements[index].describeElement('The element being rebuilt at the time was index $index of $dirtyCount');
},
);
}
index += 1;
if (dirtyCount < _dirtyElements.length || _dirtyElementsNeedsResorting) {
_dirtyElements.sort(Element._sort);
_dirtyElementsNeedsResorting = false;
dirtyCount = _dirtyElements.length;
while (index > 0 && _dirtyElements[index - 1].dirty) {
// It is possible for previously dirty but inactive widgets to move right in the list.
// We therefore have to move the index left in the list to account for this.
// We don't know how many could have moved. However, we do know that the only possible
// change to the list is that nodes that were previously to the left of the index have
// now moved to be to the right of the right-most cleaned node, and we do know that
// all the clean nodes were to the left of the index. So we move the index left
// until just after the right-most clean node.
index -= 1;
}
}
}
} finally {
for (Element element in _dirtyElements) {
assert(element._inDirtyList);
element._inDirtyList = false;
}
_dirtyElements.clear();
_scheduledFlushDirtyElements = false;
_dirtyElementsNeedsResorting = null;
Timeline.finishSync();
assert(_debugBuilding);
assert(() {
_debugBuilding = false;
_debugStateLockLevel -= 1;
if (debugPrintBuildScope)
debugPrint('buildScope finished');
return true;
}());
}
assert(_debugStateLockLevel >= 0);
}
element.mount(Element parent, dynamic newSlot)
//RenderObjectToWidgetElement
@override
void mount(Element parent, dynamic newSlot) {
assert(parent == null);
super.mount(parent, newSlot);
_rebuild();
}
该方法主要完成两个功能
- 通过super.mount(parent,newSlot)将,当前的Element添加到Element Tree中
- 通过_rebuild()把子Widget渲染出来
我们来分析下mount方法,看他是如何将当前的Element添加到Element Tree中的
//RenderObjectToWidgetElement
@override
void mount(Element parent, dynamic newSlot) {
assert(parent == null);
super.mount(parent, newSlot);
_rebuild();
}
// RootRenderObjectElement
@override
void mount(Element parent, dynamic newSlot) {
// Root elements should never have parents.
assert(parent == null);
assert(newSlot == null);
super.mount(parent, newSlot);
}
//RenderObjectElement
//完成Element添加到Element Tree后 会将RenderObject添加到RenderObject Tree中
@override
void mount(Element parent, dynamic newSlot) {
super.mount(parent, newSlot);
_renderObject = widget.createRenderObject(this);
assert(() {
_debugUpdateRenderObjectOwner();
return true;
}());
assert(_slot == newSlot);
attachRenderObject(newSlot);
_dirty = false;
}
//Element
@mustCallSuper
void mount(Element parent, dynamic newSlot) {
assert(_debugLifecycleState == _ElementLifecycle.initial);
assert(widget != null);
assert(_parent == null);
assert(parent == null || parent._debugLifecycleState == _ElementLifecycle.active);
assert(slot == null);
assert(depth == null);
assert(!_active);
_parent = parent;
_slot = newSlot;
_depth = _parent != null ? _parent.depth + 1 : 1;
_active = true;
if (parent != null) // Only assign ownership if the parent is non-null
_owner = parent.owner;
if (widget.key is GlobalKey) {
final GlobalKey key = widget.key;
key._register(this);
}
_updateInheritance();
assert(() {
_debugLifecycleState = _ElementLifecycle.active;
return true;
}());
}
总之mount的功能就是将Element和RenderObject加入到各自的渲染树中
接下来分析RenderObjectToWidgetElement._rebuild(),该方法的作用就是渲染出Element的子Widget
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);
}
}
@protected
Element updateChild(Element child, Widget newWidget, dynamic newSlot) {
assert(() {
if (newWidget != null && newWidget.key is GlobalKey) {
final GlobalKey key = newWidget.key;
key._debugReserveFor(this);
}
return true;
}());
if (newWidget == null) {
if (child != null)
//如果newWiget为null 而且child(Element)不为null,将child 和其对应的RenderObject从其对应的Tree中移除
deactivateChild(child);
return null;
}
if (child != null) {
if (child.widget == newWidget) {
if (child.slot != newSlot)
updateSlotForChild(child, newSlot);
return child;
}
if (Widget.canUpdate(child.widget, newWidget)) {
//如果新老Widget的类型相同,而且key相同,直接update
if (child.slot != newSlot)
updateSlotForChild(child, newSlot);
child.update(newWidget);
assert(child.widget == newWidget);
assert(() {
child.owner._debugElementWasRebuilt(child);
return true;
}());
return child;
}
//将子Element从树中移除,
deactivateChild(child);
assert(child._parent == null);
}
return inflateWidget(newWidget, newSlot);
}
//1.优先通过GlobalKey复用Element
//2.创建新的Element
@protected
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);//将子Element插入到父Element树中
assert(newChild._debugLifecycleState == _ElementLifecycle.active);
return newChild;
}