Flutter markNeedsLayout 、flushLayout()

116 阅读1分钟

当组件布局发生变化时,它需要调用 markNeedsLayout 方法来更新布局,它的功能主要有两个:

  1. 将自身到其 relayoutBoundary 路径上的所有节点标记为 “需要布局” 。
  2. 请求新的 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也是需要更新的
}