当组件布局发生变化时,它需要调用 markNeedsLayout
方法来更新布局,它的功能主要有两个:
- 将自身到其 relayoutBoundary 路径上的所有节点标记为 “需要布局” 。
- 请求新的 frame;在新的 frame 中会对标记为“需要布局”的节点重新布局。
看看其核心源码:
void markNeedsLayout() {
_needsLayout = true;
if (_relayoutBoundary != this) { // 如果不是布局边界节点
markParentNeedsLayout(); // 递归调用前节点到其布局边界节点路径上所有节点的方法 markNeedsLayout
} else {// 如果是布局边界节点
if (owner != null) {
// 将布局边界节点加入到 pipelineOwner._nodesNeedingLayout 列表中
owner!._nodesNeedingLayout.add(this);
owner!.requestVisualUpdate();//该函数最终会请求新的 frame
}
}
}
markNeedsLayout 执行完毕后,就会将其 relayoutBoundary 节点添加到 pipelineOwner._nodesNeedingLayout
列表中,然后请求新的 frame,新的 frame 到来时就会执行 drawFrame
方法
void drawFrame() {
pipelineOwner.flushLayout(); //重新布局
pipelineOwner.flushCompositingBits();
pipelineOwner.flushPaint();
...
}
flushLayout() 中会对之前添加到 _nodesNeedingLayout
中的节点重新布局
void flushLayout() {
while (_nodesNeedingLayout.isNotEmpty) {
final List<RenderObject> dirtyNodes = _nodesNeedingLayout;
_nodesNeedingLayout = <RenderObject>[];
//按照节点在树中的深度从小到大排序后再重新layout
for (final RenderObject node in dirtyNodes..sort((a,b) => a.depth - b.depth)) {
if (node._needsLayout && node.owner == this)
node._layoutWithoutResize(); //重新布局
}
}
}
_layoutWithoutResize
实现
void _layoutWithoutResize() {
performLayout(); // 重新布局;会递归布局后代节点
_needsLayout = false;
markNeedsPaint(); //布局更新后,UI也是需要更新的
}