Overview
Stack is a layout widget that allows you to overlay multiple children widgets.
Stack lets you place widgets on top of each other in the z-axis direction. Children are painted in order, so the last element in the array appears on top. When used with the Positioned widget, you can specify exact positions for widgets within the Stack.
See: https://api.flutter.dev/flutter/widgets/Stack-class.html
When to use it?
- When placing text or buttons over background images
- When creating floating action buttons at specific screen positions
- When overlaying badges or notifications on icons
- When displaying tags or labels on cards
- When creating custom overlay effects
Basic Usage
// Simple Stack example
const stack = Stack({
children: [
Container({ width: 200, height: 200, color: 'blue' }),
Container({ width: 150, height: 150, color: 'red' }),
Container({ width: 100, height: 100, color: 'green' })
]
});
// Using with Positioned
const positionedStack = Stack({
children: [
Container({ color: 'lightgray' }), // Background
Positioned({
top: 20,
left: 20,
child: Text('Top Left')
}),
Positioned({
bottom: 20,
right: 20,
child: Text('Bottom Right')
})
]
});
Props
children (required)
Value: Widget[]
An array of child widgets to display in the Stack. They are stacked from bottom to top in array order.
Stack({
children: [
Image({ src: 'background.jpg' }), // Bottom layer
Container({ color: 'rgba(0,0,0,0.5)' }), // Middle layer (semi-transparent overlay)
Text('Overlay Text') // Top layer
]
})
alignment
Value: Alignment (default: Alignment.topLeft)
Defines how non-positioned children are aligned within the Stack.
- Alignment.topLeft: Top-left alignment (default)
- Alignment.center: Center alignment
- Alignment.bottomRight: Bottom-right alignment
- All other Alignment values are available
Stack({
alignment: Alignment.center,
children: [
Container({ width: 200, height: 200, color: 'blue' }),
Container({ width: 100, height: 100, color: 'red' }) // Centered
]
})
fit
Value: StackFit (default: StackFit.loose)
Defines how non-positioned children should be sized within the Stack.
- StackFit.loose: Children use only the space they need (default)
- StackFit.expand: Children expand to fill the Stack
- StackFit.passthrough: Stack constraints are passed to children unchanged
Stack({
fit: StackFit.expand,
children: [
Container({ color: 'blue' }), // Expands to full Stack size
Center({
child: Text('Centered Text')
})
]
})
clipped
Value: boolean (default: false)
Determines whether to clip children that extend beyond the Stack bounds.
Stack({
clipped: true,
children: [
Container({ width: 100, height: 100, color: 'blue' }),
Positioned({
top: -10, // Outside Stack bounds
left: -10,
child: Container({ width: 50, height: 50, color: 'red' })
})
]
})
Positioned Widget
A widget that controls where a child of a Stack is positioned.
Positioned Props
- top: Distance from the top
- bottom: Distance from the bottom
- left: Distance from the left
- right: Distance from the right
- width: Explicit width
- height: Explicit height
- child: The widget to position
Positioned({
top: 10,
left: 10,
width: 100,
height: 50,
child: Container({ color: 'red' })
})
Positioned.fill()
Creates a Positioned widget that fills the entire Stack.
Stack({
children: [
Image({ src: 'background.jpg' }),
Positioned.fill({
child: Container({
color: 'rgba(0, 0, 0, 0.5)' // Semi-transparent overlay
})
})
]
})
Practical Examples
Example 1: Profile Card with Badge
const profileCard = Container({
width: 120,
height: 120,
child: Stack({
children: [
// Profile image
Container({
decoration: BoxDecoration({
shape: 'circle',
color: 'gray'
}),
child: Icon({
icon: Icons.person,
size: 80,
color: 'white'
})
}),
// Online status indicator
Positioned({
bottom: 0,
right: 0,
child: Container({
width: 30,
height: 30,
decoration: BoxDecoration({
shape: 'circle',
color: 'green',
border: Border.all({ color: 'white', width: 3 })
})
})
})
]
})
});
Example 2: Image Gallery with Overlay
const galleryItem = Container({
width: 300,
height: 200,
child: Stack({
fit: StackFit.expand,
children: [
// Background image
Image({
src: 'gallery-image.jpg',
fit: 'cover'
}),
// Gradient overlay
Positioned.fill({
child: Container({
decoration: BoxDecoration({
gradient: LinearGradient({
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: ['transparent', 'rgba(0,0,0,0.7)']
})
})
})
}),
// Text information
Positioned({
bottom: 20,
left: 20,
right: 20,
child: Column({
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Image Title', {
style: TextStyle({
color: 'white',
fontSize: 20,
fontWeight: 'bold'
})
}),
Text('By Photographer Name', {
style: TextStyle({
color: 'rgba(255,255,255,0.8)',
fontSize: 14
})
})
]
})
})
]
})
});
Example 3: Floating Action Button
const screenWithFAB = Stack({
children: [
// Main content
SingleChildScrollView({
child: Column({
children: [
// ... content widgets
]
})
}),
// Floating action button
Positioned({
bottom: 16,
right: 16,
child: Container({
width: 56,
height: 56,
decoration: BoxDecoration({
shape: 'circle',
color: 'blue',
boxShadow: [
BoxShadow({
color: 'rgba(0,0,0,0.3)',
blurRadius: 6,
offset: { x: 0, y: 3 }
})
]
}),
child: Icon({
icon: Icons.add,
color: 'white'
})
})
})
]
})
Important Notes
- The Stack’s size is determined by the largest non-positioned child.
- Positioned widgets must be direct children of Stack.
- When both
top
andbottom
are specified,height
is ignored. - When both
left
andright
are specified,width
is ignored. - When using unbounded widgets (like ListView) inside Stack, you must provide explicit size constraints.
Related Widgets
- Positioned: Controls child positioning within a Stack
- Align: A simpler way to align a single child
- IndexedStack: Shows only one child from multiple children
- CustomMultiChildLayout: For more complex layouts
- Overlay: For displaying widgets on top of the entire app