flutter-layouts-stack-positioned

Stack and Positioned in Flutter: Overlapping Elements

  • 4 min

A Stack is a widget that places elements on top of each other, like layers.

So far, we’ve been very orderly. We’ve placed things side by side (Row) or one below the other (Column). But modern design isn’t always a perfect grid.

Sometimes we need to put text on top of an image. Or a red notification circle on top of an icon. Or a floating button in a corner.

To work on this “Z-axis” (depth), Flutter offers us the Stack.

The Concept of Layers

The Stack works just like layers in Photoshop or a stack of papers on your desk.

  1. You put down the first sheet (the background).
  2. You put the second sheet on top (it covers the previous one).
  3. And so on.

In code, this means that the order in the children list matters a lot.

  • The first widget in the list is the one at the bottom (Base).
  • The last widget in the list is the one on top of everything (Top).
Stack(
  children: [
    // 1. Background (Green)
    Container(width: 300, height: 300, color: Colors.green),

    // 2. Middle layer (Red)
    Container(width: 200, height: 200, color: Colors.red),

    // 3. Topmost layer (Blue)
    Container(width: 100, height: 100, color: Colors.blue),
  ],
)
Copied!

On the Web we use z-index to control who covers whom. In Flutter there is no z-index. The paint order is defined exclusively by the order in the list.

General Alignment

By default, the Stack places everything in the top-left corner (topStart). If we want all elements to be centered or go to a corner, we use the alignment property.

Stack(
  alignment: Alignment.center, // All children are centered
  children: [
    Container(width: 300, height: 300, color: Colors.green),
    Text("Hello World"), // Will appear centered over the green square
  ],
)
Copied!

Positioned: Position Control

Alignment is fine for simple things, but what if I want the text at the bottom right and the icon at the top left?

For that, we wrap the child in a Positioned widget.

This widget only works if it is a direct descendant of a Stack. It allows us to set the position using distances: top, bottom, left, right.

Stack(
  children: [
    // Background: An image
    Container(color: Colors.blue, width: 300, height: 300),

    // Element 1: Text at the bottom (10px from the bottom edge)
    Positioned(
      bottom: 10,
      left: 10,
      child: Text("Image Title", style: TextStyle(color: Colors.white)),
    ),

    // Element 2: Icon at the top right
    Positioned(
      top: 10,
      right: 10,
      child: Icon(Icons.favorite, color: Colors.white),
    ),
  ],
)
Copied!

If you use Positioned, the child widget can change its size. If you set left: 0 and right: 0, you are forcing the child to stretch across the entire width of the Stack.

Practical Example: Notification Badge

A typical use case is the icon that indicates you have unread messages. Let’s build it:

  1. Base: The bell icon.
  2. Top layer: A small red circle positioned in the corner.
Stack(
  clipBehavior: Clip.none, // Allows the red dot to slightly overflow if needed
  children: [
    // 1. The base Icon
    Icon(Icons.notifications, size: 50, color: Colors.grey),

    // 2. The red dot
    Positioned(
      right: 0,
      top: 0,
      child: Container(
        padding: EdgeInsets.all(4),
        decoration: BoxDecoration(
          color: Colors.red,
          shape: BoxShape.circle,
        ),
        child: Text(
          '3',
          style: TextStyle(color: Colors.white, fontSize: 12),
        ),
      ),
    )
  ],
)
Copied!

How big is a Stack?

The Stack tries to adjust its size to its non-positioned children.

  • If it has a background image of 300x300 (non-positioned), the Stack will measure 300x300.
  • If all children are positioned, the Stack tries to occupy all the space allowed by its parent’s constraints. If it receives unbounded constraints, you will need to provide a size for it.

Rule of thumb: A non-positioned background child is usually the easiest way to define the size of your canvas.

Positioned.fill

A very useful shortcut. If you want an element to occupy all the space of the Stack (e.g., a background image or a semi-transparent overlay), use Positioned.fill.

Stack(
  children: [
    Positioned.fill(
      child: Image.network("...", fit: BoxFit.cover),
    ),
    Center(child: Text("Text over image")),
  ],
)
Copied!

This is equivalent to setting top: 0, bottom: 0, left: 0, right: 0.