衆所周知,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;
}