前面我们知道runApp后会经过build()->performLayout()->paint(),那么StatefulWidget 经过 setState又会经过哪些流程?
setState流程.png
onBuildScheduled()
是一个VoidCallback(无返回类型的函数)
,在WidgetsBinding
中进行了赋值
//WidgetsBinding
buildOwner.onBuildScheduled = _handleBuildScheduled;
最后调用的是native Window_scheduleFrame
方法,之后要去c++源码里寻找答案了
总之,最后会调用drawFrame()
方法:
@protected
void drawFrame() {
assert(renderView != null);
pipelineOwner.flushLayout(); //调用RenderObject的performLayout();
pipelineOwner.flushCompositingBits();
pipelineOwner.flushPaint(); //调用RenderObject的paint(PaintingContext context, Offset offset)
renderView.compositeFrame();
pipelineOwner.flushSemantics();
}
所以对应的就有四个方法
markNeedsLayout();
markNeedsCompositingBitsUpdate();
markNeedsPaint();
markNeedsSemanticsUpdate();
他们本质还是会调用drawFrame(),但是通过不同的标识,并不会进行自己对应方法之外的操作。
测量与摆放流程
通过drawFrame()查看其的flushLayout()
void flushLayout() {
profile(() {
Timeline.startSync('Layout', arguments: timelineWhitelistArguments);
});
assert(() {
_debugDoingLayout = true;
return true;
}());
try {
// TODO(ianh): assert that we're not allowing previously dirty nodes to redirty themselves
while (_nodesNeedingLayout.isNotEmpty) {
final List dirtyNodes = _nodesNeedingLayout;
_nodesNeedingLayout = [];
for (RenderObject node in dirtyNodes..sort((RenderObject a, RenderObject b) => a.depth - b.depth)) {
if (node._needsLayout && node.owner == this)
node._layoutWithoutResize(); //摆放
}
}
} finally {
assert(() {
_debugDoingLayout = false;
return true;
}());
profile(() {
Timeline.finishSync();
});
}
}
node就是一个RenderObject,然后执行他的_layoutWithoutResize()
void _layoutWithoutResize() {
assert(_relayoutBoundary == this);
RenderObject debugPreviousActiveLayout;
assert(!_debugMutationsLocked);
assert(!_doingThisLayoutWithCallback);
assert(_debugCanParentUseSize != null);
assert(() {
_debugMutationsLocked = true;
_debugDoingThisLayout = true;
debugPreviousActiveLayout = _debugActiveLayout;
_debugActiveLayout = this;
if (debugPrintLayouts)
debugPrint('Laying out (without resize) $this');
return true;
}());
try {
//找到了,执行摆放
performLayout();
markNeedsSemanticsUpdate();
} catch (e, stack) {
_debugReportException('performLayout', e, stack);
}
assert(() {
_debugActiveLayout = debugPreviousActiveLayout;
_debugDoingThisLayout = false;
_debugMutationsLocked = false;
return true;
}());
_needsLayout = false;
markNeedsPaint();
}
很明显,这里并没有执行performResize(),这里我们换个切入点,我们从runApp(rootWidget)开始
void runApp(Widget app) {
WidgetsFlutterBinding.ensureInitialized()
..attachRootWidget(app)
..scheduleWarmUpFrame();
}
ensureInitialized(),这个是一系列的绑定操作,直接看attachRootWidget(app),将我们的app控件绑定在renderView上
Element get renderViewElement => _renderViewElement;
Element _renderViewElement;
void attachRootWidget(Widget rootWidget) {
_renderViewElement = RenderObjectToWidgetAdapter(
container: renderView,
debugShortDescription: '[root]',
child: rootWidget
).attachToRenderTree(buildOwner, renderViewElement);
}
RenderObjectToWidgetAdapter是一个RenderObjectWidget,我们直接看他的createRenderObject()看创建的对象是谁
class RenderObjectToWidgetAdapter extends RenderObjectWidget {
RenderObjectToWidgetAdapter({
this.child,
this.container,
this.debugShortDescription
}) : super(key: GlobalObjectKey(container));
//省略部分代码
...
final Widget child;
final RenderObjectWithChildMixin container;
//关键点,返回的是一个container,即上面传递的renderView
@override
RenderObjectWithChildMixin createRenderObject(BuildContext context) => container;
@override
void updateRenderObject(BuildContext context, RenderObject renderObject) { }
RenderObjectToWidgetElement attachToRenderTree(BuildOwner owner, [RenderObjectToWidgetElement element]) {
//第一次是null,会创建,随后都是复用这个已经创建的element
if (element == null) {
owner.lockState(() {
element = createElement();
assert(element != null);
element.assignOwner(owner);
});
owner.buildScope(element, () {
//执行build()
element.mount(null, null);
});
} else {
element._newWidget = this;
//重新bulid()
element.markNeedsBuild();
}
return element;
}
}
而renderView是一个RenderView控件
class RenderView extends RenderObject with RenderObjectWithChildMixin {
RenderView({
RenderBox child,
@required ViewConfiguration configuration,
}) : assert(configuration != null),
_configuration = configuration {
this.child = child;
}
@override
void performResize() {
assert(false);
}
@override
void performLayout() {
assert(_rootTransform != null);
_size = configuration.size;
assert(_size.isFinite);
if (child != null)
//执行子控件(即自己的app控件)的layout()方法
child.layout(BoxConstraints.tight(_size));
}
//省略了大段内容
...
}
layout的流程是相同的,不需要重写
void layout(Constraints constraints, { bool parentUsesSize = false }) {
...
if (sizedByParent) {
assert(() { _debugDoingThisResize = true; return true; }());
try {
performResize(); //1.
assert(() { debugAssertDoesMeetConstraints(); return true; }());
} catch (e, stack) {
_debugReportException('performResize', e, stack);
}
assert(() { _debugDoingThisResize = false; return true; }());
}
RenderObject debugPreviousActiveLayout;
assert(() {
_debugDoingThisLayout = true;
debugPreviousActiveLayout = _debugActiveLayout;
_debugActiveLayout = this;
return true;
}());
try {
performLayout(); //2.
markNeedsSemanticsUpdate();
assert(() { debugAssertDoesMeetConstraints(); return true; }());
} catch (e, stack) {
_debugReportException('performLayout', e, stack);
}
assert(() {
_debugActiveLayout = debugPreviousActiveLayout;
_debugDoingThisLayout = false;
_debugMutationsLocked = false;
return true;
}());
_needsLayout = false;
markNeedsPaint();
}
当sizedByParent为true时,才会执行performResize()。默认是false的,除非继承RenderObject,将其修改为true
@override
bool get sizedByParent => true;
@override
void performResize() {
size = constraints.smallest;
}
大部分的布局控件一般都在performLayout()一起完成了测量和摆放。
sizedByParent 意为该节点的大小是否仅通过 parent 传给它的 constraints 就可以确定了,即该节点的大小与它自身的属性和其子节点无关,比如如果一个控件永远充满 parent 的大小,那么 sizedByParent就应该返回true,此时其大小在 performResize() 中就确定了,在后面的 performLayout() 方法中将不会再被修改了,这种情况下 performLayout() 只负责布局子节点
注意事项: