The Flutter Stack widget is one of the most powerful layout widgets in Flutter that allows developers to create sophisticated user interfaces by overlapping multiple widgets on top of each other. When building modern mobile applications, the Flutter Stack widget becomes essential for creating floating action buttons, badges, overlays, and complex layered designs. Understanding how to effectively use the Flutter Stack widget will significantly enhance your Flutter development skills and enable you to build more dynamic and visually appealing applications.
The Flutter Stack widget operates on a simple yet powerful principle: it positions its children relative to the edges of its box, allowing widgets to overlap and create layered effects that would be impossible with traditional linear layouts.
The Flutter Stack widget is a container widget that positions its children relative to the edges of its box. Unlike Column or Row widgets that arrange children linearly, the Flutter Stack widget allows children to overlap each other, creating a three-dimensional effect on a two-dimensional screen.
Every child widget in a Flutter Stack widget can be positioned using the Positioned
wrapper widget, which gives you precise control over where each element appears. Without positioning, children are placed at the top-left corner of the stack by default.
Stack(
children: [
Container(
width: 200,
height: 200,
color: Colors.blue,
),
Container(
width: 100,
height: 100,
color: Colors.red,
),
],
)
The alignment
property in Flutter Stack widget determines how non-positioned children are aligned within the stack. This property accepts an AlignmentGeometry
value and defaults to AlignmentDirectional.topStart
.
Stack(
alignment: Alignment.center,
children: [
Container(width: 200, height: 200, color: Colors.blue),
Container(width: 100, height: 100, color: Colors.red),
],
)
When you set alignment
to Alignment.center
, all non-positioned children will be centered within the Flutter Stack widget. Other common alignment values include Alignment.topLeft
, Alignment.bottomRight
, and Alignment.centerLeft
.
The fit
property controls how non-positioned children are sized within the Flutter Stack widget. It accepts a StackFit
enum value with three possible options: loose
, expand
, and passthrough
.
Stack(
fit: StackFit.expand,
children: [
Container(color: Colors.blue),
Positioned(
top: 50,
left: 50,
child: Container(width: 100, height: 100, color: Colors.red),
),
],
)
When fit
is set to StackFit.expand
, non-positioned children are sized to fill the entire Flutter Stack widget. The loose
option allows children to be as large as possible but not larger than the stack itself, while passthrough
passes the constraints unchanged.
The clipBehavior
property in Flutter Stack widget determines how content that overflows the stack’s boundaries is handled. This property is crucial when dealing with children that might extend beyond the stack’s dimensions.
Stack(
clipBehavior: Clip.hardEdge,
children: [
Container(width: 200, height: 200, color: Colors.blue),
Positioned(
top: -20,
left: -20,
child: Container(width: 100, height: 100, color: Colors.red),
),
],
)
The Clip.hardEdge
value ensures that any overflow is clipped at the stack’s boundaries. Other options include Clip.none
(no clipping), Clip.antiAlias
(smooth clipping), and Clip.antiAliasWithSaveLayer
(highest quality clipping).
The Positioned
widget is the key companion to the Flutter Stack widget, allowing you to specify exact coordinates for child placement. This widget provides properties for top
, bottom
, left
, right
, width
, and height
positioning.
Stack(
children: [
Container(width: 300, height: 300, color: Colors.grey[300]),
Positioned(
top: 20,
left: 20,
child: Container(width: 50, height: 50, color: Colors.red),
),
Positioned(
bottom: 20,
right: 20,
child: Container(width: 50, height: 50, color: Colors.blue),
),
],
)
The Positioned.fill()
constructor creates a positioned widget that fills the entire Flutter Stack widget. This is equivalent to setting all four directional properties (top, bottom, left, right) to zero.
Stack(
children: [
Positioned.fill(
child: Container(color: Colors.blue.withOpacity(0.3)),
),
Center(
child: Text('Overlay Text', style: TextStyle(fontSize: 24)),
),
],
)
The Positioned.fromRect()
constructor allows you to position a child using a Rect
object, which can be more intuitive when working with calculated positions.
Stack(
children: [
Container(width: 300, height: 300, color: Colors.grey),
Positioned.fromRect(
rect: Rect.fromLTWH(50, 50, 100, 100),
child: Container(color: Colors.orange),
),
],
)
The Flutter Stack widget is commonly used to create floating action buttons that appear above other content. This technique involves layering the main content with a positioned floating button.
Stack(
children: [
Container(
width: double.infinity,
height: 400,
color: Colors.grey[200],
child: Center(child: Text('Main Content Area')),
),
Positioned(
bottom: 20,
right: 20,
child: FloatingActionButton(
onPressed: () {},
child: Icon(Icons.add),
),
),
],
)
Badges are perfect examples of Flutter Stack widget usage, where you need to overlay a small indicator on top of another widget like an icon or image.
Stack(
children: [
Icon(Icons.notifications, size: 40, color: Colors.blue),
Positioned(
right: 0,
top: 0,
child: Container(
padding: EdgeInsets.all(4),
decoration: BoxDecoration(
color: Colors.red,
borderRadius: BorderRadius.circular(10),
),
child: Text('5', style: TextStyle(color: Colors.white, fontSize: 12)),
),
),
],
)
The Flutter Stack widget excels at creating image overlays, where you can add text, gradients, or other elements on top of images.
Stack(
children: [
Container(
width: 300,
height: 200,
decoration: BoxDecoration(
image: DecorationImage(
image: NetworkImage('https://example.com/image.jpg'),
fit: BoxFit.cover,
),
),
),
Positioned.fill(
child: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [Colors.transparent, Colors.black.withOpacity(0.7)],
),
),
),
),
Positioned(
bottom: 20,
left: 20,
child: Text(
'Overlay Text',
style: TextStyle(color: Colors.white, fontSize: 24, fontWeight: FontWeight.bold),
),
),
],
)
The IndexedStack
is a specialized version of the Flutter Stack widget that displays only one child at a time based on an index value. This widget maintains the state of all children while showing only the selected one.
class TabContent extends StatefulWidget {
@override
_TabContentState createState() => _TabContentState();
}
class _TabContentState extends State<TabContent> {
int _currentIndex = 0;
@override
Widget build(BuildContext context) {
return Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
ElevatedButton(
onPressed: () => setState(() => _currentIndex = 0),
child: Text('Tab 1'),
),
ElevatedButton(
onPressed: () => setState(() => _currentIndex = 1),
child: Text('Tab 2'),
),
],
),
Expanded(
child: IndexedStack(
index: _currentIndex,
children: [
Container(color: Colors.red, child: Center(child: Text('Content 1'))),
Container(color: Colors.blue, child: Center(child: Text('Content 2'))),
],
),
),
],
);
}
}
Here’s a comprehensive example demonstrating various Flutter Stack widget features in a single application:
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Stack Widget Demo',
theme: ThemeData(primarySwatch: Colors.blue),
home: StackDemoScreen(),
);
}
}
class StackDemoScreen extends StatefulWidget {
@override
_StackDemoScreenState createState() => _StackDemoScreenState();
}
class _StackDemoScreenState extends State<StackDemoScreen> {
int _notificationCount = 3;
bool _showOverlay = false;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Flutter Stack Widget Demo'),
actions: [
Padding(
padding: EdgeInsets.only(right: 20),
child: Stack(
children: [
IconButton(
icon: Icon(Icons.notifications),
onPressed: () {
setState(() {
_notificationCount = 0;
});
},
),
if (_notificationCount > 0)
Positioned(
right: 8,
top: 8,
child: Container(
padding: EdgeInsets.all(4),
decoration: BoxDecoration(
color: Colors.red,
borderRadius: BorderRadius.circular(10),
),
child: Text(
'$_notificationCount',
style: TextStyle(
color: Colors.white,
fontSize: 12,
fontWeight: FontWeight.bold,
),
),
),
),
],
),
),
],
),
body: Stack(
children: [
// Main content
Container(
width: double.infinity,
height: double.infinity,
padding: EdgeInsets.all(20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Flutter Stack Widget Examples',
style: Theme.of(context).textTheme.headlineSmall,
),
SizedBox(height: 20),
// Card with image overlay
Container(
width: double.infinity,
height: 200,
child: Stack(
children: [
Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
gradient: LinearGradient(
colors: [Colors.purple, Colors.blue],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
),
),
Positioned.fill(
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
Colors.transparent,
Colors.black.withOpacity(0.6)
],
),
),
),
),
Positioned(
bottom: 20,
left: 20,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Beautiful Gradient Card',
style: TextStyle(
color: Colors.white,
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
Text(
'Created with Flutter Stack Widget',
style: TextStyle(
color: Colors.white70,
fontSize: 14,
),
),
],
),
),
Positioned(
top: 10,
right: 10,
child: Icon(
Icons.favorite,
color: Colors.red,
size: 30,
),
),
],
),
),
SizedBox(height: 30),
ElevatedButton(
onPressed: () {
setState(() {
_showOverlay = !_showOverlay;
});
},
child: Text(_showOverlay ? 'Hide Overlay' : 'Show Overlay'),
),
],
),
),
// Floating Action Button
Positioned(
bottom: 30,
right: 30,
child: FloatingActionButton(
onPressed: () {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('FAB pressed!')),
);
},
child: Icon(Icons.add),
),
),
// Overlay
if (_showOverlay)
Positioned.fill(
child: Container(
color: Colors.black.withOpacity(0.8),
child: Center(
child: Container(
margin: EdgeInsets.all(40),
padding: EdgeInsets.all(30),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(15),
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
Icons.info_outline,
size: 50,
color: Colors.blue,
),
SizedBox(height: 20),
Text(
'This is an Overlay',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
SizedBox(height: 10),
Text(
'Created using Flutter Stack Widget with Positioned.fill',
textAlign: TextAlign.center,
style: TextStyle(fontSize: 16),
),
SizedBox(height: 20),
ElevatedButton(
onPressed: () {
setState(() {
_showOverlay = false;
});
},
child: Text('Close'),
),
],
),
),
),
),
),
],
),
);
}
}
This comprehensive example demonstrates multiple Flutter Stack widget implementations including notification badges, image overlays, floating action buttons, and modal overlays. The application showcases how the Flutter Stack widget can create complex, layered user interfaces that would be difficult to achieve with other layout widgets.
The Flutter Stack widget is an indispensable tool for modern Flutter development, enabling developers to create sophisticated user interfaces with overlapping elements, floating components, and layered designs. By mastering the Flutter Stack widget and its companion Positioned
widget, you can build more engaging and visually appealing applications that stand out in today’s competitive mobile app market.