skip to content

In Flutter, what do a Stack's fit and clipBehavior control, and why is an avatar badge with a negative offset cut off?

level: middleimportance: should knowfreq 45%

answer

  1. only non-positioned children
  2. loose, expand, passthrough
  3. clipBehavior defaults to Clip.hardEdge
  4. direct children's geometry only
  5. Clip.none lets it spill

basics

~20 s

fit sets the constraints non-positioned children get: loosened (default), tightened to the biggest size, or passed through. clipBehavior defaults to Clip.hardEdge, so a Positioned badge with a negative inset that overflows the Stack is clipped; Clip.none lets it spill.

solid answer

~40 s

`fit` affects only non-positioned children. `StackFit.loose` (the default) loosens the incoming constraints, so a child can be smaller than the Stack; `StackFit.expand` tightens them to the biggest size, forcing every non-positioned child to fill the Stack; `StackFit.passthrough` hands the incoming constraints on unchanged, which matters when, say, the Stack is an `Expanded` child with a tight width but loose height. `clipBehavior` defaults to `Clip.hardEdge`. During layout the Stack records whether any direct child's box extends past its bounds, and if so it clips painting to its rectangle. A badge at `Positioned(right: -4, bottom: -4)` extends 4 pixels past the edge, so its outer ring is cut off. `clipBehavior: Clip.none` lets it draw outside. Only direct children's geometry triggers the clip, so a shadow on a child that fits is not clipped.

go deeper

for a junior

Recall that fit decides how big non-positioned children may be and that a Stack clips overflowing children by default.

for a middle

Explain the three StackFit values as constraint transformations and how the Stack decides to clip from its direct children's geometry.

for a senior

Choose between clipping, spilling and enlarging the Stack for badges and floating controls, weighing paint cost and hit-testing consequences.

for a principal

Define component guidance for decorative spill versus interactive overlays so teams do not ship controls that look tappable but are not.

## Two independent knobs A `Stack` has two parameters that people confuse because both deal with 'how much room children get': - **`fit`** changes the **constraints** given to non-positioned children during layout. - **`clipBehavior`** changes **painting**: whether content outside the Stack's rectangle is cut off. Neither changes how positioned children are constrained; their constraints always come from their own insets. ## fit: StackFit values | Value | Non-positioned children receive | Typical use | |---|---|---| | `StackFit.loose` (default) | the incoming constraints loosened: minimums zero, maximums kept | children keep their natural size, like an avatar with a badge | | `StackFit.expand` | tight constraints at the biggest allowed size | a full-bleed background image plus overlays that must fill the area | | `StackFit.passthrough` | the incoming constraints unchanged | keep a parent's mix, such as tight width and loose height from an `Expanded` in a `Row` | Examples from the framework documentation: 1. With `loose` and incoming tight 350 by 600 constraints, children may be anything from 0 to 350 wide and 0 to 600 tall. 2. With `expand` and incoming width 10 to 100, height 0 to 600, every non-positioned child becomes exactly 100 by 600. 3. With `passthrough`, a Stack that is an `Expanded` child of a `Row` passes a tight width and a loose height straight through. `StackFit.expand` is a common surprise: a small icon placed as a non-positioned child suddenly fills the whole Stack. Wrap it in `Align` or make it `Positioned` if it should keep its size. ## clipBehavior: what gets cut off `clipBehavior` defaults to **`Clip.hardEdge`**. The mechanism is narrower than 'clip everything outside': - While placing positioned children, the Stack checks whether any **direct child's box** extends past its bounds (a negative inset, or an offset plus size larger than the Stack) and records a visual-overflow flag. - At paint time it clips to its rectangle **only when that flag is set** and `clipBehavior` is not `Clip.none`. - Content that paints outside its own box without changing geometry, such as a `BoxShadow` or a `Transform`, is not detected. If no child's box overflows, that content is not clipped. A descendant deeper down that overflows is not detected either. The other values are `Clip.antiAlias` and `Clip.antiAliasWithSaveLayer`, which clip with smoothed edges at higher cost, and `Clip.none`, which never clips. ## The avatar badge Designers often want the online-status dot to sit half outside the avatar's circle: 1. `Positioned(right: -4, bottom: -4, child: statusDot)` places the dot 4 pixels past the Stack's corner. 2. The dot's box overflows, so the flag is set. 3. With the default `Clip.hardEdge`, the Stack clips to its 72 by 72 rectangle and the dot's outer ring disappears. 4. `clipBehavior: Clip.none` draws the full dot. There is a catch: `Clip.none` changes painting only. Taps on the part of a child drawn outside the Stack's bounds are not delivered, because hit testing still stops at the Stack's rectangle. For a decorative dot that is fine; for a tappable camera button it is a bug. ## Choosing values - Keep `StackFit.loose` unless every non-positioned layer must fill the area. - Keep `Clip.hardEdge` when overflow is a mistake you want contained; use `Clip.none` for deliberate spill that is decorative. - Prefer enlarging the Stack, for example with `Padding` around the avatar, over spilling interactive content.

  • In Flutter, how can Align's widthFactor help size an avatar Stack with room for a badge?
    `Align` (and `Center`) normally grow as large as their constraints allow, but with `widthFactor` or `heightFactor` set they size to the child times the factor. `Center(widthFactor: 1.2, heightFactor: 1.2, child: avatar)` is 20 percent larger than the avatar with the avatar centred, so a non-negative `Positioned` badge in a Stack around it sits inside the bounds.
  • Why might a BoxShadow on a Stack child still be visible outside the Stack with the default Clip.hardEdge?
    The Stack decides to clip only when a direct child's geometry, its laid-out box, extends past the Stack's bounds. A shadow paints outside the box without changing the geometry, so if every child's box fits, no clip is applied and the shadow shows. Add a `ClipRect` if it must be cut.

saying these in an interview costs you the question

  • StackFit.expand also stretches Positioned children to fill the Stack.
  • A Stack never clips its children by default.
  • Clip.hardEdge clips shadows even when no child overflows.
  • Clip.none also makes the spilled area respond to taps.
  • StackFit.passthrough always forces children to the Stack's size.