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.

  1. ListView/ScrollPhysics
  2. TextFields
  3. FloatingActionButtons
  4. Hero Widget
  5. Transform Widget
  6. Draggable/DragTarget
  7. PageView

In this article, we will take a look at the Stack widget and the types of it.

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:

  1. Stack
  2. 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(),
],
),
A visual representation of the layers of the Stack

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:

An example of the Stack widget

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,
),
)
Stack + Align

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:

Stack + Positioned

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.

IndexedStack with index 0

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

Example use of Stack in an app

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.

Feel free to check out my other profiles and articles as well

Some of my other articles