In Flutter, what do a Stack's fit and clipBehavior control, and why is an avatar badge with a negative offset cut off?
answer
- only non-positioned children
- loose, expand, passthrough
- clipBehavior defaults to Clip.hardEdge
- direct children's geometry only
- Clip.none lets it spill
basics
~20 sfit 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
Recall that fit decides how big non-positioned children may be and that a Stack clips overflowing children by default.
Explain the three StackFit values as constraint transformations and how the Stack decides to clip from its direct children's geometry.
Choose between clipping, spilling and enlarging the Stack for badges and floating controls, weighing paint cost and hit-testing consequences.
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.