Layout

The original address: www.flutterinternals.org/rendering/l…

What is Relayout Boundary?

  • Relayout boundary is a logical division point between nodes in render tree. Nodes below the boundary never invalidate the layout of nodes above the boundary.
  • Relayout boundary is expressed as a render object (RenderObject._relayoutBoundary), allowing it to be laid out independently of its parent. In other words, the subtree with relayout boundary as the following node cannot affect other parts of the tree.
    • If the parent of a Render Object ignores its size (! parentUseSize), fully define the size of the node (sizedByParent), passing strict constraints, or it is not a Render object (e.gRenderView), the node is defined as a relayout boundary (i.e. cannot influence the layout of the parent node).
    • The latter two cases show that no matter what happens to the subtree, the child nodes cannot change size.
    • parentUsesSizeThe parameter prevents the Render object from becoming a relayout boundary. Therefore, when the render object is marked as dirty, all ancestor nodes (including the parent node) will be marked as dirty up to the nearest Relayout boundary.
  • Relayout Boundary limits how many ancestor nodes must be marked dirty when a Render object needs to be rearranged. The traversal will stop at the nearest ancestral node relayout boundary because the nodes beyond this point are not affected by the layout of descendants.
  • Relayout boundary will be ignored when it comes to actual layout. Render Object always has a Layout child node. However, the traversal stops when the child node is not marked dirty or receives the same constraint (which does not require a layout).
    • RenderObject.layoutResponsible for executing this logic.RenderObject._layoutWithoutResizeExecute layout unconditionally becausePipelineOwner.flushLayoutClean nodes have been skipped.

How does the layout mark dirty?

  • PipelineOwnerMaintain a list of dirty nodes (PipelineOwner._nodeNeedingLayout),PipelineOwner.flushLayoutMethod clears the list every frame.
  • Marking Render Objects as needing to be laid out is batch manipulable (that is, you can process the layout in a single traverse rather than laying out the nodes multiple times).
  • When usingRenderObject.markNeedsLayoutSet up theRenderObject._needsLayoutTo mark a Render object as dirty, all ancestor nodes under the nearest Relayout boundary (including Relayout Boundary) will also be marked as dirtyRenderObject.markParentNeedsLayout).
    • Only the relayout boundary in the nearest closed interval will be addedPipelineOwner._nodeNeedingLayoutIn the. All descendant nodes iterate over the execution layout. This also requests a callback for the next frame (passPipelineOwner.requestVisualUpdate) to allow layout changes to be applied (throughPipelineOwner.flushLayout).
    • When processing nodes added to the dirty list earlier, nodes added later may become clean.PipelineOwner.flushLayoutIgnore the clean node.
  • ifsizedByParentIf the tag changes, the Render Object and its parent must be notified (throughRenderObject.markNeedsLayoutForSizedByParentChange). Update this flag to add or remove relayout boundary in the render tree (changes are then made byRenderObject.layoutSubmitted). Most Render Objects do not update this tag dynamically.
    • Nodes in the Dirty list are usedRenderObject._layoutWithoutResizeThis does not update the Relayout boundary so the parent node must be marked as dirty. Therefore, since the state of the child node may change, you must layout its parent node as well.
    • Changing this flag bit also means that the child nodes will take on new dimensions, potentially invalidating the parent node’s layout.

How are constraints passed?

  • Configure from view (ViewConfiguration.size) while reading,RenderViewProvides the top-level constraints (throughRenderView.performLayout). This represents the size of the entire visual interface in logical pixels.
  • Render objects marked dirty use the previous cached constraint (RenderObject.constraints) perform the layout. These constraints cannot be invalid. Otherwise, nodes at the top of the tree will be marked as dirty.
  • The size of the Render object (defined byRenderObject.performResizeRenderObject.performLayoutMay affect the constraints provided to child nodes. Constraints can also be completely arbitrary (for example, OverflowBox). These constraints are forwarded to any child nodes, and the process is recursively repeated.
    • Constraints are cached and usedRenderObject.layout

How is Layout implemented?

  • PipelineOwner.flushLayoutThe dirty List () is triggered in order of increasing depthPipelineOwner._nodesNeedingLayoutLayout for all nodes in. The nodes in this list are equivalent to relayout boundary, which callsRenderObject._layoutWithoutResizeRather thanRenderObject.layoutFor the layout.
    • Nodes in the Dirty List never need to be calledRenderObject.performResize. Consider a node whose size is determined by its parent.
      • If the parent node is not dirty, the incoming constraints does not change. Therefore, resize is unnecessary.
      • If the parent node is marked dirty, it is laid out first because the node is cleaned from top to bottom. This will eventually lay out the original node (which may now be recalculated), effectively removing it from the dirty list.
    • RenderObject._layoutWithoutResizeDoes not update cached constraints (RenderObject.constraints) or relayout boundary (RenderObject._relayoutBoundary).
      • Since the ancestor node has not yet been marked dirty, the incoming constraint does not change.
      • Since the relationship between this node and its parent node has not changed, the Relayout boundary state will not change. If changed, the parent node is also marked as dirty (passedRenderObject.markNeedsLayoutForSizedByParentChange).
  • When a child node is marked dirty, receives a new constraint, or has just become relayout boundary, the layout (throughRenderObject.layout). Otherwise, it marks clean and breaks the traversal.
    • Note that layout optimization is applied in the marking phase, not the layout phase. Relayout boundaries provide a limit but no other immediate effect when a child is marked as a dirty node.
    • RenderObject.layoutCaching new constraints (RenderObject.constraints), and relayout boundary field (RenderObject._relayoutBoundary) to the affected descendant node (throughRenderObject._cleanChildRelayoutBoundary).
  • Size is adjusted by its parent (RenderObject.sizedByParentRender Objects () only determines the new size by passing in the constraintRenderObject.performResize). The layout of all Render Objects (including those that have been resized) continuesRenderObject.performLayout).
    • This method is responsible for laying out all the child nodes (throughRenderObject.layout) and update the parent data of all child nodes.
    • Depending on how the parent performs layout (for example, when the size of the parent node affects size, its children must be laid out first), the child node may be laid out before or after the parent node.
    • Render objects whose size is not determined by the parent must be inRenderObject.performLayoutTo determine a new size.
  • After completion,RenderObject.layoutMarking nodes as requiring semantics and painting, but not layout.

How to optimize layout?

  • There are certain conditions to avoid laying out child nodes:
    • If the child nodes are not dirty and the constraints are constant, no layout is required, so traversal can be interrupted.
    • If the parent node does not use the size of the child node, it does not need to be laid out with the child node. The child node must conform to the original passed constraint, so nothing happens to the husband node.
    • If a parent node passes tight constraints to its children, it does not need to be laid out with the children. Child nodes cannot be resized.
    • If the child node isSizeByParent(the given constraint completely determines its size), and the same constraint is passed again, then the parent node does not need to be laid out with the child node; Child nodes cannot be resized.
  • The nearest upstream relayout boundary will be maintained when layout down the tree. When executing the layout (passRenderObject.performLayout), render Object determines whether it meets any of the above optimization criteria. If it does, it sets itself to the nearest Relayout boundary. Otherwise, it will adopt the parent’s Relayout boundary.
    • The layout continues after relayout boundary is updated, even if the nodes are clean and the incoming constraints remain unchanged.
    • All child nodes must update the nearest relayout boundary that they know of. This is done by recursive access to clear the Relayout boundary and mark the Render object as needed for layout (throughRenderObject._cleanRelayoutBOundary). Traversal stops at the offspring of their self-delineated Relayout boundary (i.eRenderObject._relayoutBoundary == this).
    • Nodes whose layout may have been invalid (that is, the assumption that the layout could have been skipped is no longer valid) must be marked as dirty. The layout is also responsible for passing relayout boundary down the affected Render Object tree.

How do I build the UI to respond to the layout?

  • RenderObject.invokeLayoutCallbackAllows a Buidler callback to be called during layout. You can only operate on subtrees that are still dirty to ensure that the nodes are laid out exactly once. This allows you to build child nodes (viewports) and move nodes (global key relocations) as needed during the layout process.
  • The following invariants ensure that this is valid:
    • The build is one-way — information flows only down the tree, visiting one node at a time.
    • The layout accesses each dirty node at once.
    • The build below the current child node cannot invalidate the previous layout; Building information just flows down the tree.
    • Whether to invalidate a node after the node is laid out; The node is never revisited.
    • Therefore, it is safe to build in an unaccessed subtree rooted in the given Render Object.