Exploring Stack and IndexedStack in Flutter
Exploring the Stack and IndexedStack widget and their uses
This article is eighth in the series of articles which take a look at Flutter built-in widgets.
- ListView/ScrollPhysics
- TextFields
- FloatingActionButtons
- Hero Widget
- Transform Widget
- Draggable/DragTarget
- PageView
In this article, we will take a look at the Stack
widget and the types of it.
NEW: I made a short video to introduce the Stack Widget -
Introduction
A Stack
widget allows us to make multiple widgets overlay each other. This not only allows brilliant custom designs but also some really cool animations.
There are two main types of Stacks:
Stack
IndexedStack
Let’s explore the use of Stacks.
Stack
The Stack
widget allows us to put up multiple layers of widgets onto the screen.
The widget takes multiple children and orders them from bottom to top. So the first item is the bottommost and the last is the topmost.
Stack(
children: <Widget>[
BottomWidget(),
MiddleWidget(),
TopWidget(),
],
),
The size of the Stack
is the size of the largest member in the layer. So if the bottom layer covers the complete screen then the size of the Stack is the complete screen.
Each member in the stack needs to be positioned or aligned, or else it ends up in the top left corner by default.
As an example, let’s take three containers of shrinking size:
Stack(
children: <Widget>[
// Max Size
Container(
color: Colors.green,
),
Container(
color: Colors.blue,
height: 300.0,
width: 300.0,
),
Container(
color: Colors.pink,
height: 150.0,
width: 150.0,
)
],
),
This gives us:
If you notice, the containers which are smaller have a lot more area to go to and hence default to top left. To change this, you can align or position your widget using the Align
or Positioned
widget.
An Align
widget usually takes widget to extreme positions. So for example, if we enter top-right, we need to add extra padding to keep it neat and tidy. A Positioned
widget combines these two things and lets us keep one Positioned
widget instead of an Align
and a Padding
. We will see how in a while.
We’ll alter our example to use Align
and Positioned
. Let’s simply wrap our containers in an Align
and then a Positioned
widget.
Note: Positioned
has to be a child of a Stack
. There cannot be another widget in between the stack and the widget.
To simply align a widget:
Align(
alignment: Alignment.topRight,
child: Container(
color: Colors.pink,
height: 150.0,
width: 150.0,
),
)
Here we align it to the top right. Usually, this isn’t what we actually want since we need some padding to the container from the edges. We can either use padding with Align
or Positioned
for more fine-grained control.
Positioned
has multiple types, we’ll focus on the default.
It accepts values from all four sides telling the child how far it should be away from that respective side. If no value is given, it shrinks to the lowest possible size.
Here, we give a value to the top and right. In effect, what this means is that the child will be 40.0 away from top and right and no other constraints on the other sides. Hence, it is also aligned to the top and right sides by definition.
Positioned(
right: 40.0,
top: 40.0,
child: Container(
color: Colors.pink,
height: 150.0,
width: 150.0,
),
)
This gives us:
Hence, Positioned
is a better widget to use in a Stack
than Align
+ Padding
but there’s no real harm in either.
There are also other types of Positioned. A few of them are:
Positioned.fill()
Sets top, right, bottom, left to 0.0 by default unless overriden. Hence, it fills the screen by default since distance from all four sides is 0.0.
Positioned.fromRect()
Creates a Positioned object from a given Rect.
Before we move on to creating something with Stack, let’s take a look at the second type.
IndexedStack
An IndexedStack is a stack where only one element is displayed at one time by its index.
IndexedStack(
index: 1,
children: <Widget>[
BottomWidget(),
MidWidget(),
TopWidget(),
],
)
It takes children like a usual Stack
but in contrast to it, only displays one child at one time. In a way, it’s not a stack and more of a way to easily switch between children when you need to.
Let’s see an example:
IndexedStack(
index: 0,
children: <Widget>[
Container(
color: Colors.green,
),
Container(
alignment: Alignment.bottomLeft,
color: Colors.blue,
height: 300.0,
width: 300.0,
),
Container(
color: Colors.pink,
height: 150.0,
width: 150.0,
)
],
),
When we wrap the same elements with IndexedStack
and give it the index
0, it shows the bottommost child.
The upper two children are not shown as the index
is 0.
Note: The size is still the size of the largest element.
IndexedStack
also directly takes an alignment
parameter and aligns all children accordingly.
IndexedStack(
index: 1,
children: <Widget>[
//Children
],
alignment: Alignment.topRight,
)
Let’s make something with Stack
This is a relatively straightforward example of where we can use a Stack.
An image here is overlaid by a card which is positioned at the bottom centre.
Step 1) Add a Stack
Stack(
children: <Widget>[
],
)
Step 2) Add a background
Here, I’m simply using a container with an image inside it
Container(
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage('images/new_york.jpg'),
fit: BoxFit.fitHeight,
),
),
),
Step 3) Add a card on top with the details
Card(
elevation: 8.0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8.0),
),
child: Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.all(16.0),
child: Text(
"New York",
style: TextStyle(
fontSize: 20.0,
fontWeight: FontWeight.bold,
),
),
),
Padding(
padding: const EdgeInsets.all(16.0),
child: Text(
"Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book."),
),
],
),
),
Step 4) Position the card correctly
Positioned(
bottom: 48.0,
left: 10.0,
right: 10.0,
child: Card(
elevation: 8.0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8.0),
),
child: Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.all(16.0),
child: Text(
"New York",
style: TextStyle(
fontSize: 20.0,
fontWeight: FontWeight.bold,
),
),
),
Padding(
padding: const EdgeInsets.all(16.0),
child: Text(
"Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book."),
),
],
),
),
),
And we’re done!
The final code is:
Some of my experiments with Stack
Stacks are more or less ubiquitous in Flutter apps, but I’ve tried to use them in a few different ways (Mostly combined with the Transform widget). Here are a few projects of mine you can take a look at.
Making a 3D box for a BottomNavigationBar
The items in the BottomNavigationBar are made using a Stack and Transform widget.
Link: https://medium.com/flutter-community/flutter-challenge-3d-bottom-navigation-bar-48952a5fd996
Making a Card stack for Solitaire in Flutter
Each of the column of cards here is made using a Stack.
Link: https://medium.com/flutter-community/creating-solitaire-in-flutter-946c34ef053c
Making the YouTube Picture-In-Picture video screen
Here a Stack is used to make the floating video screen.
Link: https://proandroiddev.com/flutter-challenge-youtube-ec5ff36eca9b
That’s it for this article! I hope you enjoyed it, and leave a few claps if you did. Follow me for more Flutter articles and comment for any feedback you might have about this article.