Rows and columns line widgets up; sometimes you need widgets to sit on top of each other instead. A badge on an avatar, a caption over an image, a floating action button above a card, a gradient scrim under text — these are all jobs for Stack and Positioned. They work like layers in an image editor: each child paints over the one before it, and you place children precisely with offsets. This guide covers layering, alignment, fit, clipping, and z-order, with runnable code throughout.

The Complete Flutter Guide: Build Android, iOS and Web apps
Go from scratch to building industry-standard apps with Riverpod, Firebase, animations, REST APIs, and more.
Enrol nowEvery snippet is paste-ready against current stable Flutter. Stack is one of the most useful layout widgets once you know its rules, and one of the most misused before that. For the wider layout picture, this pairs with the Flutter Layout Widgets guide.
We will cover how a Stack sizes itself, how it aligns non-positioned children, how Positioned and Positioned.fill place layers exactly, z-order, clipping with clipBehavior, and a worked image-card example — finishing with the common mistakes.
This tutorial is part of a series on Flutter's core widgets. Its closest companions are the Container guide and the Row and Column guide — the boxes and lines you layer here, and animating those layers is covered in the Flutter Animations guide.
How a Stack works
A Stack paints its children in order: the first child is the bottom layer, and each later child sits on top. Children come in two kinds — non-positioned (plain widgets) and positioned (wrapped in Positioned). The Stack sizes itself to contain its non-positioned children, then aligns those children according to its alignment. Positioned children are placed by their offsets and do not affect the Stack's size.
Stack(
children: [
Container(width: 200, height: 200, color: Colors.indigo.shade100), // bottom layer
const Icon(Icons.cloud, size: 64), // on top, aligned
],
)
Aligning non-positioned children
The alignment property positions every non-positioned child within the Stack. The default is AlignmentDirectional.topStart (top-left in left-to-right locales). Set it to Alignment.center or any other alignment to move them as a group.
Stack(
alignment: Alignment.center,
children: [
Container(width: 160, height: 160, color: Colors.teal.shade100),
const Text('Centred over the box'),
],
)
Positioned: place a child precisely
Wrap a child in Positioned to pin it to the Stack's edges with top, right, bottom, and left. You usually set two or three of them; setting both left and right (or top and bottom) also stretches the child between those edges.
Stack(
children: [
Container(width: 200, height: 120, color: Colors.amber.shade200),
Positioned(
top: 8,
right: 8,
child: Container(
padding: const EdgeInsets.all(6),
decoration: const BoxDecoration(color: Colors.red, shape: BoxShape.circle),
child: const Text('3', style: TextStyle(color: Colors.white)),
),
),
],
)
That is the classic notification badge: a box at the bottom, a small circle pinned to its top-right corner.

Build layered, polished UI
The Complete Flutter Guide takes you from overlays and badges to shipping real, beautiful apps on every platform.
Enrol nowPositioned.fill: stretch a layer over everything
Positioned.fill sets all four edges to zero by default, so the child fills the whole Stack. It is the cleanest way to add a full-bleed background, a gradient scrim, or a transparent tap layer.
Stack(
children: [
Image.network('https://picsum.photos/400/240', fit: BoxFit.cover),
Positioned.fill(
child: DecoratedBox(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.bottomCenter,
end: Alignment.center,
colors: [Colors.black54, Colors.transparent],
),
),
),
),
const Positioned(
left: 12,
bottom: 12,
child: Text('Caption', style: TextStyle(color: Colors.white, fontSize: 18)),
),
],
)
Z-order: who sits on top
There is no z-index in Flutter. Layering follows the order of the children list: earlier children are painted first and sit underneath; the last child sits on top. To bring a widget forward, move it later in the list. To send it back, move it earlier.
If you need to change which layer is on top dynamically — say, a selected card rising above its siblings — reorder the children based on state, or use an IndexedStack when you want to show only one child at a time while keeping the others alive.
Clipping with clipBehavior
By default a Stack clips anything extending past its bounds (Clip.hardEdge). Two adjustments are common:
clipBehavior: Clip.nonelets children overflow visibly — needed when a badge or avatar should poke outside the edge.- Wrap the Stack in a
ClipRRectto round the clip, for example to give an image card rounded corners that also clip the overlay.
Stack(
clipBehavior: Clip.none, // let the badge overflow the top edge
children: [
const CircleAvatar(radius: 28, child: Icon(Icons.person)),
Positioned(
top: -6,
right: -6,
child: Container(
padding: const EdgeInsets.all(4),
decoration: const BoxDecoration(color: Colors.green, shape: BoxShape.circle),
child: const Icon(Icons.check, size: 12, color: Colors.white),
),
),
],
)
One gotcha worth remembering: a child that overflows a Stack with Clip.none is painted, but taps outside the Stack's own bounds may not register, because hit-testing is bounded by the Stack. Keep interactive elements inside the Stack's area.
A worked example: an image card with overlay
This combines everything — a background image, a gradient scrim via Positioned.fill, a caption pinned to the bottom, and a badge in the corner — clipped to rounded corners.
ClipRRect(
borderRadius: BorderRadius.circular(16),
child: Stack(
children: [
Image.network('https://picsum.photos/400/240', width: 400, height: 240, fit: BoxFit.cover),
Positioned.fill(
child: DecoratedBox(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.bottomCenter,
end: Alignment.center,
colors: [Colors.black87, Colors.transparent],
),
),
),
),
const Positioned(
left: 16,
bottom: 16,
child: Text('Mountain trail',
style: TextStyle(color: Colors.white, fontSize: 20, fontWeight: FontWeight.bold)),
),
Positioned(
top: 12,
right: 12,
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4),
decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(20)),
child: const Text('New'),
),
),
],
),
)
Common Stack and Positioned mistakes
- An unsized Stack with only positioned children. If every child is
Positioned, the Stack has nothing to size to and may collapse — give it a bounded size (aSizedBoxor a sized first child). - Expecting
z-index. Reorder thechildrenlist to change layering; there is no z-index property. - Forgetting
clipBehavior: Clip.none. Badges that should overflow the edge get clipped by default. - Putting
Positionedoutside a Stack.Positionedonly works as a direct child of aStack(orFlow); elsewhere it throws. - Layering when a simpler widget exists. For a row of avatars or a one-at-a-time view,
Wrap,Badge, orIndexedStackmay read better than a hand-built Stack.
Frequently asked questions
What is a Stack in Flutter?
A layout widget that places children on top of one another in layers. The first child is at the back and each later child paints over it. Non-positioned children are aligned by the Stack; Positioned children sit at exact offsets.
What is the difference between Positioned and Positioned.fill?
Positioned places a child at specific top/right/bottom/left offsets. Positioned.fill sets all four to zero, stretching the child to cover the whole Stack — ideal for backgrounds and overlays.
How does z-order work in a Stack?
It follows child order: the first child is at the back, the last is on top. Move a widget later in the children list to bring it forward. There is no z-index.
How do I clip widgets that overflow a Stack?
A Stack clips with Clip.hardEdge by default. Use clipBehavior: Clip.none to let children overflow, or wrap the Stack in a ClipRRect for rounded clipping.
Further reads
Keep going with the tutorials that pair with this guide:
- Flutter Development Guide 2026 — the full Flutter hub.
- Flutter Layout Widgets: The Complete Guide — every layout widget and the constraints rule.
- Flutter Container Widget Explained — the boxes you layer in a Stack.
- Flutter Row and Column — line widgets up instead of layering them.
- Flutter Animations: The Complete Guide — animate positioned layers with AnimatedPositioned.
Sources: Flutter documentation — Stack, Positioned, IndexedStack, and Clip API references, plus the Layout guide (docs.flutter.dev). Verified against current stable Flutter.