Box Model

What are the render box building blocks?

  • RenderBox models a box in 2D cartesian coordinates with a width, height, and offset. The origin of the box is the top-left corner (0,0), with the bottom-right corner corresponding to (width, height).

  • BoxParentData stores a child’s position in the parent’s coordinate space. It is opaque to the child by convention.

  • BoxConstraints provide cartesian constraints in the form of a maximum and minimum width and height between 0 and infinity, inclusive. Constraints are satisfied for all dimensions simultaneously within the given inclusive range.

    • Constraints are normalized when min is less or equal to max.

    • Constraints are tight when max and min are equal.

    • Constraints are loose when min is 0, even if max is also 0 (loose and tight).

    • Constraints are bounded when max is not infinite.

    • Constraints are unbounded when max is infinite.

    • Constraints are expanding when tightly infinite.

    • Constraints are infinite when min is infinite (max must also be infinite; thus, this is also expanding).

  • BoxHitTestResult is an aggregator that collects the entries associated with a single hit test query. BoxHitTestResult includes several box-specific helpers, i.e., BoxHitTestResult.addWithPaintOffset, BoxHitTestResult.addWithPaintTransform, etc.

  • BoxHitTestEntry represents a box that was hit during hit testing. It captures the position of the collision in local coordinates (BoxHitTestEntry.localPosition).

What are the render box tree building blocks?

  • RenderProxyBox supports a single RenderBox child, delegating all methods to said child. RenderShiftedBox is identical, but offsets the child using BoxParentData.offset and requires a layout implementation. Both are built with RenderObjectWithChildMixin.

  • RenderObjectWithChildMixin and ContainerRenderObjectMixin can both be used with a type argument of RenderBox. The ContainerRenderObjectMixin accepts a parent data type argument, as well; ContainerBoxParentData satisfies the type constraints and supports box parent data.

  • RenderBoxContainerDefaultsMixin adds useful defaults to ContainerRenderObjectMixin for render boxes. This includes support for hit testing and painting children, as well as computing baselines.

How do render boxes handle layout?

  • RenderBox layout is the same as RenderObject layout, with the input being BoxConstraints and the output being RenderBox.size (Size). Layout typically also sets the position (BoxParentData.offset) of any children, though the child may not read this data.

  • The RenderBox protocol includes support for intrinsic dimensions (a size that is computed outside of the layout protocol) as well as baselines (the bottom of the box for vertical alignment). RenderBox instances track changes to these values and whether the parent has queried them; if so, when that box is marked as needing layout, the parent is marked as well.

  • Marking a render box as needing layout implies that the box needs paint. Building a render box implies both.

  • Render boxes can have non-box children. In this case, the constraints passed from the parent to the child will need to be adapted from BoxConstraints to the new protocol.

How do render boxes handle paint?

  • Painting is the same as RenderObject painting. The offset provided corresponds to the origin of the render object in the canvas’s coordinate space (which may not be the same).

  • If the render box applies a transform when painting, including painting at a different offset than the one provided, RenderBox.applyPaintTransform must apply the same transformation. RenderBox.globalToLocal and RenderBox.localToGlobal rely on this transform to map cartesian coordinates.

    • By default, RenderBox.applyPaintTransform will apply the child’s offset to the matrix as a translation.

How do render boxes handle hit testing?

  • All RenderBoxes must implement RenderBox.hitTest, which by default delegates to RenderBox.hitTestChildren and RenderBox.hitTestSelf, in that order. Note that items added to the BoxHitTestResult first are treated as being on top of those added later. All entries in the BoxHitTestResult are fed events from the corresponding event stream via RenderBox.handleEvent.

What are intrinsic dimensions?

  • Conceptually, the intrinsic dimensions of a box are its natural dimensions -- the size it “wants” to be. Given that this definition is subjective, it is left as an implementation detail what exactly this means for a given render object. Note that intrinsic dimensions are often defined in terms of child intrinsic dimensions and are therefore expensive to calculate (typically traversing an entire subtree).

  • Intrinsic dimensions often do not match the dimensions resulting from layout (except when using the IntrinsicHeight and IntrinsicWidth widgets, which attempt to layout their child using its intrinsic dimensions).

    • Intrinsic dimensions are generally ignored unless one of these widgets are used (or another widget that explicitly incorporates intrinsic dimensions into its own layout).

  • The render box model describes intrinsic dimensions in terms of minimum and maximum values for width and height.

    • Minimum intrinsic width is the smallest width before the box cannot paint correctly without clipping.

      • Intuition: making the box thinner would clip its contents.

      • If width is determined by height (ignoring constraints), the incoming height (which may be infinite, i.e., unconstrained) should be used. Otherwise, ignore the height.

    • Minimum intrinsic height is the same concept for height.

    • Maximum intrinsic width is the smallest width such that further expansion would not reduce minimum intrinsic height (for that width).

      • Intuition: making the box wider won’t help fit more content.

      • If width is determined by height (ignoring constraints), the incoming height (which may be infinite, i.e., unconstrained) should be used. Otherwise, ignore the height.

    • Maximum intrinsic height is the same concept for height.

  • The specific meaning of intrinsic dimensions depends on the implementation.

    • Text is width-in-height-out.

      • Max intrinsic width: the width of the string without line breaks (increasing the width would not shrink the preferred height).

      • Min intrinsic width: the width of the widest word (decreasing the width would clip the word or cause an invalid break).

      • Height intrinsics derived from width intrinsics for the given width.

    • Viewports ignore incoming constraints and aggregate child dimensions without clipping.

    • Aspect ratio boxes are entirely determined when one dimension is provided.

      • Use the incoming dimension to compute the other. If unconstrained, recurse.

    • When intrinsic dimensions cannot be computed or are too expensive, return zero.

What are baselines?

  • Baselines are used to vertically align children independent of font size, padding, etc. When a real baseline isn’t available, the bottom of the render box is used. Baselines are expressed as an offset from the box’s y-coordinate; if there are multiple children, the first sets the baseline.

  • Only a render box’s parent can invoke RenderBox.getDistanceToBaseline, and only after that render box has been laid out in the parent’s RenderBox.performLayout method.

Last updated