Stack and Positioned Widgets
Published April 2026
Introduction
In the standard Flutter layout system, widgets are arranged linearly using Row and Column layout grids. While this flow-based system covers most user interface scenarios, you will inevitably need to overlay components on top of each other. Common examples include placing a badge notification count on top of an icon, overlaying text on top of an image card, or creating a profile cover screen with a floating avatar image.
To achieve overlapping layouts, Flutter provides the Stack and Positioned widgets. Rather than arranging children sequentially along a linear path, a Stack layers its children on top of each other. The order of rendering is from bottom to top; the first widget declared in the children list is placed at the bottom, and subsequent widgets are painted on top of it.
Understanding how stacks handle sizing and coordinates is crucial. Unlike rows and columns that operate on logical flex factors, Stacks rely on constraints and absolute layout coordinates. In this guide, you will learn the theory of stack overlay alignments, review positioning parameters, and build a beautiful, compiler-valid profile banner.
Prerequisites
Before building overlapping layouts, ensure you have:
- Read our guide on Understanding Widgets in Flutter.
- Read our Row vs. Column Layout Guide.
- Your local development server or emulator running (refer to Your First Flutter App for setup details).
Core Explanation: Overlapping Layers & Coordinates
A Stack widget takes a list of children and layers them. The sizing and layout positioning of these children depend on whether they are Positioned or Non-positioned.
┌──────────────────────────────────────────┐
│ STACK VIEWPORT │
│ (0,0) │
│ ┌──────────────────────────────┐ │
│ │ Base Widget (Non-Positioned) │ │
│ │ │ │
│ │ (Positioned) │ │
│ │ ┌────────────┐ │ │
│ │ │ top: 40 │ │ │
│ │ │ left: 80 │ │ │
│ │ └────────────┘ │ │
│ └──────────────────────────────┘ │
└──────────────────────────────────────────┘
1. Non-Positioned Children
A child widget is "non-positioned" if it is not wrapped in a Positioned widget. By default, the Stack sizes itself to fit the dimensions of its largest non-positioned child. All non-positioned children are aligned inside the stack boundary using the alignment property (e.g. Alignment.center, Alignment.bottomRight).
2. Positioned Children
A child widget is "positioned" when wrapped in a Positioned widget. The Positioned widget allows you to define absolute distances from the borders of the stack container using four properties:
top: Distance from the top edge.bottom: Distance from the bottom edge.left: Distance from the left edge.right: Distance from the right edge.
If you specify both left and right coordinates on a widget, the widget is forced to stretch horizontally to satisfy both parameters. The same stretching logic applies vertically if both top and bottom are set.
3. Sizing Constraints: StackFit
The fit property of the Stack controls how parent constraints are passed to its children:
StackFit.loose: Children are allowed to be any size within the parent's maximum constraints (default).StackFit.expand: Forces all non-positioned children to expand and fill the maximum allowed dimensions.StackFit.passthrough: Passes the parent constraints directly to the children without modification.
Practical Example: Composing a Profile Header Banner
Let's build a clean, compiler-ready Profile Header containing a background cover photo, an overlapping user avatar, and a floating online status badge.
import 'package:flutter/material.dart';
/// A custom profile header banner illustrating Stack and Positioned coordinate layouts.
class ProfileHeaderBanner extends StatelessWidget {
final String coverImageUrl;
final String avatarImageUrl;
final String userName;
final bool isOnline;
const ProfileHeaderBanner({
super.key,
required this.coverImageUrl,
required this.avatarImageUrl,
required this.userName,
required this.isOnline,
});
@override
Widget build(BuildContext context) {
return Column(
mainAxisSize: MainAxisSize.min,
children: [
// Container wrapping our Stack layout
SizedBox(
height: 220,
child: Stack(
clipBehavior: Clip.none, // Allows avatar avatar to overflow the stack boundaries
children: [
// Layer 1: Background Cover Photo (Non-positioned child sizing the width)
Positioned.fill(
child: ClipRRect(
borderRadius: const BorderRadius.vertical(
top: Radius.circular(16.0),
),
child: Image.network(
coverImageUrl,
fit: BoxFit.cover,
),
),
),
// Layer 2: Visual Gradient overlay on top of cover image
Positioned.fill(
child: DecoratedBox(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
Colors.black.withOpacity(0.1),
Colors.black.withOpacity(0.5),
],
),
),
),
),
// Layer 3: Overlapping Profile Avatar placing at bottom-center boundary
Positioned(
bottom: -40, // Offsets the avatar image halfway outside the container boundary
left: 0,
right: 0,
child: Center(
child: Stack(
clipBehavior: Clip.none,
children: [
// Avatar border casing
Container(
decoration: BoxDecoration(
shape: BoxShape.circle,
border: Border.all(color: Colors.white, width: 4.0),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.15),
blurRadius: 8,
offset: const Offset(0, 4),
),
],
),
child: CircleAvatar(
radius: 50,
backgroundImage: NetworkImage(avatarImageUrl),
backgroundColor: Colors.grey[200],
),
),
// Layer 4: Online Status Badge Indicator inside avatar stack
Positioned(
bottom: 4,
right: 4,
child: Container(
height: 18,
width: 18,
decoration: BoxDecoration(
color: isOnline ? Colors.green : Colors.grey,
shape: BoxShape.circle,
border: Border.all(color: Colors.white, width: 2.5),
),
),
),
],
),
),
),
],
),
),
// Dynamic spacer representing offset placeholder margin
const SizedBox(height: 52.0),
// Profile username title below banner stack
Text(
userName,
style: const TextStyle(
fontSize: 22,
fontWeight: FontWeight.bold,
letterSpacing: 0.5,
),
),
],
);
}
}
Code Explanation:
clipBehavior: Clip.none: By default, Stacks clip any children that overflow their boundaries. SettingClip.noneallows the profile avatar to hang halfway outside the cover image.Positioned.fill: A helper constructor that expands the cover image and the black gradient decoration to fill the entire width and height of the stack.bottom: -40: Vertically offsets the avatar container down past the bottom edge of the cover background.- Nested Stacks: The status badge is placed inside a nested
Stackwrapping theCircleAvatarto ensure the status indicator aligns precisely with the avatar's bottom-right quadrant.
Common Mistakes
1. Using Positioned Outside a Stack
Attempting to wrap a widget in Positioned inside standard layout flow components (like a Row, Column, or padding container) will trigger a compiler or layout build crash.
- The Fix: Ensure that
Positionedwidgets are immediate children of aStackwidget.
2. Sizing Collapses (Unconstrained Stacks)
If a stack container has no non-positioned children and has no parent layout constraints defining its dimensions, it will collapse to a width/height of zero.
- The Fix: Define strict dimensions on the parent container (like wrapping the stack in a
SizedBox) or make sure the stack contains at least one non-positioned widget that has clear dimensions.
3. Tap Interception Issues
Because widgets are layered on top of each other, invisible transparent layers can intercept tap events, preventing users from clicking buttons underneath them.
- The Fix: Use
IgnorePointerorAbsorbPointeron decorative/transparent overlay widgets to let clicks pass through to interactive buttons below.
Best Practices
- Explicitly Configure Clip Actions: Set
clipBehavior: Clip.nonewhen building overflow configurations like user cards or sliding drawers to prevent layout clipping. - Use
Positioned.fillfor Backgrounds: Avoid manual coordinate properties (top: 0, left: 0) for backgrounds. UsePositioned.fillto automatically expand background decorations. - Favor Composition Over Absolute Stacks: Do not rely on Stacks for linear layout alignment. Stacks are expensive to compile and compute compared to rows and columns; only use them when overlapping UI components is necessary.
Related Concepts
Continue master layout mechanics by exploring these guides:
- Understanding Widgets in Flutter: Learn the difference between layouts and the rendering layer.
- Row vs. Column Layout Guide: Master space distribution along cross and main axes.
- Building Responsive Layouts: Learn to construct layouts that scale across web and tablet viewports.
Summary
Stacklayers widgets sequentially from bottom to top based on their declaration order.Positionedwidgets allow you to set absolute distances (top,bottom,left,right) from the boundaries of the parentStack.- Configure
clipBehavior: Clip.noneon aStackto allow elements to overflow its boundaries. - Stacks determine their sizes from the dimensions of their non-positioned child widgets.
Next Steps
- Previous Guide: Row vs. Column Layout Guide
- Next Guide: Building Responsive Layouts
- Related Guide: Stack and Positioned Widgets
Ready to take the next step?
Start our free Flutter course and build real apps today.
Start Learning Now