Widget Layering and Positioning with Stack
Just as you use CSS position: absolute
for absolute positioning on the web, in Flitter you can use Stack and Positioned widgets to layer widgets and position them exactly where you want.
🎯 Learning Objectives
After completing this tutorial, you’ll be able to:
- Layer widgets using Stack
- Specify exact positions with Positioned
- Specify relative positions with Align
- Understand and apply z-index concepts
- Create practical overlay UIs
📚 Understanding the Stack Widget
Stack layers child widgets on top of each other. Widgets added later are drawn on top.
Basic Stack Structure
Stack({
alignment: Alignment.center, // Default alignment
fit: StackFit.loose, // Size adjustment method
children: [
// First widget (bottom layer)
// Second widget
// Third widget (top layer)
]
})
StackFit Options
StackFit.loose
- Stack sizes to fit its children (default)StackFit.expand
- Stack takes up all available space from parentStackFit.passthrough
- Passes parent constraints to children
🎨 Understanding the Positioned Widget
Positioned specifies the exact position of a child widget within a Stack.
Positioned Properties
Positioned({
top: 10, // Distance from top
right: 20, // Distance from right
bottom: 30, // Distance from bottom
left: 40, // Distance from left
width: 100, // Width specification (optional)
height: 50, // Height specification (optional)
child: Container({ /* ... */ })
})
🚀 Practice 1: Basic Stack Layout
Stack({
children: [
// Background
Container({
width: 300,
height: 200,
color: '#e3f2fd'
}),
// Center element
Positioned({
left: 50,
top: 50,
child: Container({
width: 100,
height: 100,
color: '#2196f3',
child: Center({ child: Text("Center") })
})
}),
// Top right element
Positioned({
right: 10,
top: 10,
child: Container({
width: 40,
height: 40,
color: '#f44336',
child: Center({ child: Text("!") })
})
})
]
})
🚀 Practice 2: Creating a Profile Card
Container({
width: 300,
height: 180,
child: Stack({
children: [
// Background image replacement
Container({
decoration: new BoxDecoration({
gradient: new LinearGradient({
colors: ['#4a90e2', '#7b68ee'],
begin: Alignment.topCenter,
end: Alignment.bottomCenter
})
})
}),
// Profile image position
Positioned({
left: 20,
bottom: 20,
child: Container({
width: 80,
height: 80,
decoration: new BoxDecoration({
color: 'white',
shape: BoxShape.circle,
border: Border.all({ color: 'white', width: 3 })
}),
child: Center({ child: Text("👤", { style: new TextStyle({ fontSize: 40 }) }) })
})
}),
// Name and description
Positioned({
left: 120,
bottom: 50,
child: Column({
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text("John Doe", {
style: new TextStyle({
color: 'white',
fontSize: 24,
fontWeight: 'bold'
})
}),
Text("UI/UX Designer", {
style: new TextStyle({
color: 'rgba(255,255,255,0.8)',
fontSize: 16
})
})
]
})
}),
// Follow button
Positioned({
right: 20,
bottom: 20,
child: Container({
padding: EdgeInsets.symmetric({ horizontal: 20, vertical: 10 }),
decoration: new BoxDecoration({
color: 'white',
borderRadius: BorderRadius.circular(20)
}),
child: Text("Follow", {
style: new TextStyle({
color: '#4a90e2',
fontWeight: 'bold'
})
})
})
})
]
})
})
🚀 Practice 3: Icon with Notification Badge
Container({
width: 60,
height: 60,
child: Stack({
children: [
// Icon background
Container({
width: 60,
height: 60,
decoration: new BoxDecoration({
color: '#f5f5f5',
shape: BoxShape.circle
}),
child: Center({
child: Text("🔔", { style: new TextStyle({ fontSize: 30 }) })
})
}),
// Notification badge
Positioned({
right: 0,
top: 0,
child: Container({
width: 20,
height: 20,
decoration: new BoxDecoration({
color: '#f44336',
shape: BoxShape.circle,
border: Border.all({ color: 'white', width: 2 })
}),
child: Center({
child: Text("3", {
style: new TextStyle({
color: 'white',
fontSize: 12,
fontWeight: 'bold'
})
})
})
})
})
]
})
})
💡 Practical Example: Image Gallery Overlay
Container({
width: 350,
height: 250,
child: Stack({
children: [
// Background image (replaced with color)
Container({
decoration: new BoxDecoration({
gradient: new RadialGradient({
colors: ['#ffd89b', '#19547b'],
center: Alignment.center,
radius: 1.5
})
})
}),
// Gradient overlay
Container({
decoration: new BoxDecoration({
gradient: new LinearGradient({
colors: ['transparent', 'rgba(0,0,0,0.7)'],
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
stops: [0.5, 1.0]
})
})
}),
// Title and description
Positioned({
left: 20,
bottom: 20,
right: 20,
child: Column({
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text("Beautiful Sunset", {
style: new TextStyle({
color: 'white',
fontSize: 24,
fontWeight: 'bold'
})
}),
SizedBox({ height: 8 }),
Text("A fantastic sunset captured at Hyeopjae Beach, Jeju Island", {
style: new TextStyle({
color: 'rgba(255,255,255,0.8)',
fontSize: 14
})
})
]
})
}),
// Like button
Positioned({
right: 20,
top: 20,
child: Container({
width: 40,
height: 40,
decoration: new BoxDecoration({
color: 'rgba(255,255,255,0.3)',
shape: BoxShape.circle
}),
child: Center({
child: Text("❤️", { style: new TextStyle({ fontSize: 20 }) })
})
})
})
]
})
})
🎨 Practice Exercises
- Floating Action Button: Create a circular button fixed to the bottom right
- Tooltip Overlay: Implement a tooltip that appears on hover
- Image Text Overlay: Place text and gradients over images
🚨 Important Notes
- Positioned only inside Stack: Must be a direct child of Stack
- Overflow handling: Be careful with elements extending outside Stack
- z-index: The order in the children array acts as z-index
🔗 Next Steps
- Padding and Margin - Controlling spacing
- Expanded and Flexible - Space distribution
💡 Key Summary
- Stack: Layers widgets on top of each other
- Positioned: Provides precise positioning within Stack
- z-index: Determined by order in children array
- StackFit: Controls how Stack sizes itself
- Overlay patterns: Great for badges, floating buttons, and image overlays
Stack and Positioned give you the power to create sophisticated layered UIs!