Flutter Lesson 18 of 83 3 min read
Stack and Positioned in Flutter
Learn Stack in Flutter: overlap widgets, place them with Positioned and Align, and build badges, banners and image overlays.
On this page
Row and Column put widgets next to each other. Stack puts them on top of each other. Use it for a badge on an icon, text over a photo, or a button floating above a list.
Basics #
Children are painted in order: the first is at the back, the last is in front.
import 'package:flutter/material.dart';
void main() {
runApp(
MaterialApp(
home: Scaffold(
body: Center(
child: Stack(
children: [
Container(width: 220, height: 220, color: Colors.indigo),
Container(width: 150, height: 150, color: Colors.amber),
Container(width: 80, height: 80, color: Colors.red),
],
),
),
),
),
);
}
All three sit in the top-left corner. Add alignment: Alignment.center to the Stack to centre them.
Positioned #
Wrap a child in Positioned to pin it to the edges of the stack.
Stack(
children: [
Image.network('https://picsum.photos/400/250', fit: BoxFit.cover),
const Positioned(
left: 16,
bottom: 16,
child: Text(
'Pokhara',
style: TextStyle(color: Colors.white, fontSize: 24, fontWeight: FontWeight.bold),
),
),
Positioned(
top: 8,
right: 8,
child: IconButton.filledTonal(
onPressed: () {},
icon: const Icon(Icons.favorite_border),
),
),
],
)
Positioned | Effect |
|---|---|
top, left, right, bottom | Distance from that edge |
left: 0, right: 0 | Stretch across the full width |
width, height | Fixed size |
Positioned.fill(child: ...) | Cover the whole stack |
How big is the Stack? #
A stack sizes itself to fit its non-positioned children. Positioned children do not affect its size. So a stack needs at least one normal child, or an explicit size from its parent, such as a SizedBox.
A notification badge #
Stack(
clipBehavior: Clip.none, // let the badge poke outside
children: [
const Icon(Icons.notifications, size: 32),
Positioned(
top: -4,
right: -4,
child: Container(
padding: const EdgeInsets.all(4),
decoration: const BoxDecoration(color: Colors.red, shape: BoxShape.circle),
child: const Text('3', style: TextStyle(color: Colors.white, fontSize: 11)),
),
),
],
)
By default a stack clips anything outside its bounds. clipBehavior: Clip.none allows the overlap. For this exact job Flutter also has a ready-made Badge widget:
const Badge(label: Text('3'), child: Icon(Icons.notifications))
Readable text over an image #
Text straight on a photo is often hard to read. Put a gradient between them.
Stack(
fit: StackFit.expand,
children: [
Image.network(url, fit: BoxFit.cover),
const DecoratedBox(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [Colors.transparent, Colors.black87],
),
),
),
const Align(
alignment: Alignment.bottomLeft,
child: Padding(
padding: EdgeInsets.all(16),
child: Text('Annapurna Base Camp', style: TextStyle(color: Colors.white, fontSize: 20)),
),
),
],
)
StackFit.expand forces non-positioned children to fill the stack.
Align inside a Stack #
Align is an alternative to Positioned when you want a relative position, not a pixel distance.
A loading overlay #
Stack(
children: [
const MyForm(),
if (_saving)
const Positioned.fill(
child: ColoredBox(
color: Colors.black38,
child: Center(child: CircularProgressIndicator()),
),
),
],
)
IndexedStack #
IndexedStack shows only one of its children but keeps them all alive. It is used with bottom navigation so that each tab keeps its scroll position.
IndexedStack(
index: _currentTab,
children: const [HomeTab(), SearchTab(), ProfileTab()],
)
When not to use Stack #
If the widgets do not overlap, use Row and Column. Positioning everything by pixel breaks on other screen sizes. Reach for Stack only for real overlap.
Try it yourself #
Build a profile banner: a wide cover image 160 pixels tall, with a circular avatar overlapping its bottom edge by half, and an edit button in the top-right corner.