If a child is not dirty and the constraints are unchanged, it needn’t be laid out, cutting off the walk.
If a parent does not use a child’s size, it needn’t be laid out with the child; the child will necessarily conform to the original input constraints, and thus nothing changes for the parent.
If a parent passes tight constraints to its child, it needn’t be laid out with the child; the child cannot change size.
If a child is “sizedByParent
” (the constraints solely determine the size), and the same constraints are passed, the parent needn’t be laid out with the child; the child cannot change size.
RenderObject.invokeLayoutCallback
allows a builder callback to be invoked during the layout process. Only subtrees that are still dirty may be manipulated which ensures that nodes are laid out exactly once. This allows children to be built on-demand during layout (needed for viewports) and nodes to be moved (needed for global key reparenting).
Several invariants ensure that this is valid:
Building is unidirectional -- information only flows down the tree, visiting each node once.
Layout visits each dirty node once.
Building below the current node cannot invalidate earlier layout; build information only flows down the tree.
Building after a node is laid out cannot invalidate that node; nodes are never revisited.
Therefore, it is safe to build within the unvisited subtree rooted at a given render object.
Container provides a consistent interface to a number of more fundamental layout building blocks. Unless otherwise specified, each box is sized to match its child.
ConstrainedBox
applies an extra set of constraints in addition to the incoming constraints. The extra constraints are clamped to the incoming constraints (via BoxConstraints.enforce
) and thus may not violate them. The box becomes as small as possible without a child.
SizedBox
delegates to ConstrainedBox
, providing tight constraints for the specified dimensions. The result is that the child will be sized to these dimensions subject to the incoming constraints. The box is still sized even without a child.
UnconstrainedBox
lays out its child with unbounded constraints (in one or more dimensions). It then sizes itself to match the child subject to incoming constraints (i.e., to prevent any actual overflow). An alignment is applied if the child is a different size than its parent. If there would have been overflow, the child’s painting is clipped accordingly. The box becomes as small as possible without a child.
OverflowBox
allows one or more components of the incoming constraints to be completely overwritten. The child is laid out using the altered constraints and therefore may overflow. An alignment is applied if the child is a different size than its parent. In all other ways, this box delegates to the child.
SizedOverflowBox
assumes a specific size, subject to the incoming constraints. Its child is laid out using the original constraints, however, and therefore may overflow the parent (e.g., if the incoming constraints permit a size of up to 100x100, and the parent specifies a size of 50x50, the child can select a larger size, overflowing its parent). Since this box has an explicit size, its given intrinsic dimensions matching this size.
FractionallySizedBox
sizes its child in proportion to the incoming constraints; it sizes itself to the child, but without violating the original constraints. In particular, the maximum constraints are multiplied by a corresponding factor to obtain a tight bound (if a factor isn’t specified, that dimension’s constraints are left unchanged). The child is laid out using the new constraints and may therefore overflow the parent. The parent is sized to the child subject to the incoming constraints, and therefore cannot overflow; note that painting and hit testing will not be automatically clipped. Intrinsic dimensions are defined as the child’s intrinsic dimension scaled by the corresponding factor.
LimitedBox
applies a maximum width or height only when the corresponding constraint is unbounded. That is, when the incoming constraints are forwarded to the child, the maximum constraint in each dimension is set to a specific value if it is otherwise unbounded. This is typically used to place expanding children into containers with infinite dimensions (e.g., viewports).
Padding applies spacing around its child (via EdgeInsetsGeometry
). This is achieved by providing the child with constraints deflated by the padding. The parent assumes the size of the child (which is zero if there is no child) inflated by the padding.
EdgeInsetsGeometry
describes offsets in the four cardinal directions. EdgeInsets
specifies the offsets with absolute positioning (e.g., left, top, right, bottom) whereas EdgeInsetsDirectional
is text-orientation sensitive (e.g., start, top, end, bottom). Both allow offsets to be combined, subtracted, inflated, and deflated, as well as queried along an axis.
Padding defines intrinsic dimensions by reducing the incoming dimension by the total applicable padding before querying the child; the total padding is then added to the resulting dimension.
Transform applies a transformation matrix during painting. It does not alter layout, delegating entirely to RenderProxyBox
for these purposes. If the transform can solely be described as a translation, it delegates to its parent for painting, as well. Otherwise, it composites the corresponding transformation before painting its child. Optionally, this transform may be applied before hit testing (via Transform.transformHitTests
).
FittedBox
applies a BoxFit
to its child. BoxFit
specifies how a child box is to be inscribed within a parent box (e.g., BoxFit.fitWidth
scales the width to fit the parent and the height to maintain the aspect ratio, possibly overflowing the parent; BoxFit.scaleDown
aligns the child within the parent then scales to ensure that the child does not overflow). The child is laid out without constraint, then scaled to be as large as possible while maintaining the original aspect ratio and respecting the incoming constraints. The box fit determines a transformation matrix that is applied during painting.
The box fit is calculated by applyBoxFit
, which considers each box’s size without respect to positioning. The result is a FittedSizes
instance, which describes a region within the child (the source) and a region within the parent (the destination). In particular, the source measures the portion of the child visible within the parent (e.g., if this is smaller than the child, the child will be clipped). The destination measures the portion of the parent occupied by the child (e.g., if this is smaller than the parent, a portion of the parent will remain visible).
The sizes are transformed into rectangles using the current alignment (via Alignment.inscribe
). Finally, a transformation is composed to translate and scale the child relative to the parent as per the calculated rectangles.
Baseline aligns a child such that its baseline is coincident with its own (as specified). The child’s baseline is obtained via RenderBox.getDistanceToBaseline
and an offset calculated to ensure the child’s baseline is coincident with the target line (itself expressed as an offset from the box’s top). As a result, though the parent is sized to contain the child, the offset may cause the parent to be larger (e.g., if the child is shifted below the parent’s top).
Align allows a child to be aligned within a parent according to a relative position specified via AlignmentGeometry
. During layout, the child is positioned according to the alignment (via RenderAligningShiftedBox.alignChild
, which invokes AlignmentGeometry.alongOffset
; this maps the alignment to logical coordinates using simple geometry). Note that the child must undergo layout beforehand since its size is needed to compute its center; the incoming constraints are loosened before being forwarded to the child. Next, the parent is sized according to whether a width or height factor was specified; if so, the corresponding dimension will be set to a multiple of that of the child (subject to the incoming constraints). Otherwise, it will expand to fill the constraints. If the incoming constraints are unbounded, the parent will instead shrinkwrap the child.
Alignment is a subclass of AlignmentGeometry
that describes a point within a container using units relative to the container’s size and center. The child’s center is positioned such that it is coincident with this point. Each unit is measured as half the container’s corresponding dimension (e.g., the container is effectively 2 units wide and 2 units tall). The center of the container corresponds to (0, 0), the top left to (-1, -1), and the bottom right to (1, 1). Units may overflow this range to specify different multiple of the parent’s size.
AlignmentDirectional
is identical but flips the x-axis for right-to-left languages.
FractionalOffset
represents a relative position as a fraction of the container’s size, measured from its top left corner. The child’s top-left corner is positioned such that it is coincident with this point. The center of the container corresponds to (0.5
, 0.5
), the top left to (0, 0), and the bottom right to (1, 1).
IntrinsicHeight
and IntrinsicWidth
integrate the intrinsic dimension and layout protocols. Intrinsic dimensions reflect the natural size of an object; this definition varies based on what the object actually represents. Prior to laying out, the child’s intrinsic dimension is queried (e.g., via RenderBox.getMaxIntrinsicWidth
). Next, the incoming constraints are tightened to reflect the resulting value. Finally, the child is laid out using the tight constraints. In this way, the child’s intrinsic size is used in determining its actual size (the two will only match if permitted by the incoming constraints).
Boxes often define their intrinsic dimensions recursively. Consequently, an entire subtree may need to be traversed to produce one set of dimensions. This can quickly lead to polynomial complexity, particularly if intrinsic dimensions are computed several times during layout (which itself traverses the render tree).
Stack
LayoutBuilder
makes it easy to take advantage of interleaving the build / layout phase via callbacks. The callback has access to the incoming constraints (i.e., because the framework has completed building and is currently performing layout) but may still build additional UI (via RenderObject.invokeLayoutCallback
). Once building is complete, layout continues through the subtree and beyond.
CustomSingleChildLayout
allows its layout and its child’s layout to be customized without implementing a new render box. Deferring to a delegate, the associated RenderCustomSingleChildLayout
invokes SingleChildLayoutDelegate.getSize
to size the parent, then SingleChildLayoutDelegate.getConstraintsForChild
to constrain the child, and finally SingleChildLayoutDelegate.getPositionForChild
to position the child.
CustomMultiChildLayout
is similar, but supports multiple children. To identify children, each must be tagged with a LayoutId
, a ParentDataWidget
that stores an identifier in each child’s MultiChildLayoutParentData
(recall that render objects establish their children’s parent data type). MultiChildLayoutDelegate
behaves similarly to its single-child variant; override MultiChildLayoutDelegate.performLayout
to size and position each child via MultiChildLayoutDelegate.layoutChild
and MultiChildLayoutDelegate.positionChild
, respectively (the latter must be called once per child in any order).
ConstrainedLayoutBuilder
LayoutBuilder
SliverLayoutBuilder