Overview

Stack is a layout widget that allows you to overlay multiple children widgets.

Stack lets you place widgets on top of each other in the z-axis direction. Children are painted in order, so the last element in the array appears on top. When used with the Positioned widget, you can specify exact positions for widgets within the Stack.

See: https://api.flutter.dev/flutter/widgets/Stack-class.html

When to use it?

  • When placing text or buttons over background images
  • When creating floating action buttons at specific screen positions
  • When overlaying badges or notifications on icons
  • When displaying tags or labels on cards
  • When creating custom overlay effects

Basic Usage

// Simple Stack example
const stack = Stack({
  children: [
    Container({ width: 200, height: 200, color: 'blue' }),
    Container({ width: 150, height: 150, color: 'red' }),
    Container({ width: 100, height: 100, color: 'green' })
  ]
});

// Using with Positioned
const positionedStack = Stack({
  children: [
    Container({ color: 'lightgray' }), // Background
    Positioned({
      top: 20,
      left: 20,
      child: Text('Top Left')
    }),
    Positioned({
      bottom: 20,
      right: 20,
      child: Text('Bottom Right')
    })
  ]
});

Props

children (required)

Value: Widget[]

An array of child widgets to display in the Stack. They are stacked from bottom to top in array order.

Stack({
  children: [
    Image({ src: 'background.jpg' }), // Bottom layer
    Container({ color: 'rgba(0,0,0,0.5)' }), // Middle layer (semi-transparent overlay)
    Text('Overlay Text') // Top layer
  ]
})

alignment

Value: Alignment (default: Alignment.topLeft)

Defines how non-positioned children are aligned within the Stack.

  • Alignment.topLeft: Top-left alignment (default)
  • Alignment.center: Center alignment
  • Alignment.bottomRight: Bottom-right alignment
  • All other Alignment values are available
Stack({
  alignment: Alignment.center,
  children: [
    Container({ width: 200, height: 200, color: 'blue' }),
    Container({ width: 100, height: 100, color: 'red' }) // Centered
  ]
})

fit

Value: StackFit (default: StackFit.loose)

Defines how non-positioned children should be sized within the Stack.

  • StackFit.loose: Children use only the space they need (default)
  • StackFit.expand: Children expand to fill the Stack
  • StackFit.passthrough: Stack constraints are passed to children unchanged
Stack({
  fit: StackFit.expand,
  children: [
    Container({ color: 'blue' }), // Expands to full Stack size
    Center({
      child: Text('Centered Text')
    })
  ]
})

clipped

Value: boolean (default: false)

Determines whether to clip children that extend beyond the Stack bounds.

Stack({
  clipped: true,
  children: [
    Container({ width: 100, height: 100, color: 'blue' }),
    Positioned({
      top: -10, // Outside Stack bounds
      left: -10,
      child: Container({ width: 50, height: 50, color: 'red' })
    })
  ]
})

Positioned Widget

A widget that controls where a child of a Stack is positioned.

Positioned Props

  • top: Distance from the top
  • bottom: Distance from the bottom
  • left: Distance from the left
  • right: Distance from the right
  • width: Explicit width
  • height: Explicit height
  • child: The widget to position
Positioned({
  top: 10,
  left: 10,
  width: 100,
  height: 50,
  child: Container({ color: 'red' })
})

Positioned.fill()

Creates a Positioned widget that fills the entire Stack.

Stack({
  children: [
    Image({ src: 'background.jpg' }),
    Positioned.fill({
      child: Container({ 
        color: 'rgba(0, 0, 0, 0.5)' // Semi-transparent overlay
      })
    })
  ]
})

Practical Examples

Example 1: Profile Card with Badge

const profileCard = Container({
  width: 120,
  height: 120,
  child: Stack({
    children: [
      // Profile image
      Container({
        decoration: BoxDecoration({
          shape: 'circle',
          color: 'gray'
        }),
        child: Icon({ 
          icon: Icons.person, 
          size: 80,
          color: 'white' 
        })
      }),
      // Online status indicator
      Positioned({
        bottom: 0,
        right: 0,
        child: Container({
          width: 30,
          height: 30,
          decoration: BoxDecoration({
            shape: 'circle',
            color: 'green',
            border: Border.all({ color: 'white', width: 3 })
          })
        })
      })
    ]
  })
});
const galleryItem = Container({
  width: 300,
  height: 200,
  child: Stack({
    fit: StackFit.expand,
    children: [
      // Background image
      Image({
        src: 'gallery-image.jpg',
        fit: 'cover'
      }),
      // Gradient overlay
      Positioned.fill({
        child: Container({
          decoration: BoxDecoration({
            gradient: LinearGradient({
              begin: Alignment.topCenter,
              end: Alignment.bottomCenter,
              colors: ['transparent', 'rgba(0,0,0,0.7)']
            })
          })
        })
      }),
      // Text information
      Positioned({
        bottom: 20,
        left: 20,
        right: 20,
        child: Column({
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Text('Image Title', {
              style: TextStyle({ 
                color: 'white', 
                fontSize: 20, 
                fontWeight: 'bold' 
              })
            }),
            Text('By Photographer Name', {
              style: TextStyle({ 
                color: 'rgba(255,255,255,0.8)', 
                fontSize: 14 
              })
            })
          ]
        })
      })
    ]
  })
});

Example 3: Floating Action Button

const screenWithFAB = Stack({
  children: [
    // Main content
    SingleChildScrollView({
      child: Column({
        children: [
          // ... content widgets
        ]
      })
    }),
    // Floating action button
    Positioned({
      bottom: 16,
      right: 16,
      child: Container({
        width: 56,
        height: 56,
        decoration: BoxDecoration({
          shape: 'circle',
          color: 'blue',
          boxShadow: [
            BoxShadow({
              color: 'rgba(0,0,0,0.3)',
              blurRadius: 6,
              offset: { x: 0, y: 3 }
            })
          ]
        }),
        child: Icon({ 
          icon: Icons.add, 
          color: 'white' 
        })
      })
    })
  ]
})

Important Notes

  • The Stack’s size is determined by the largest non-positioned child.
  • Positioned widgets must be direct children of Stack.
  • When both top and bottom are specified, height is ignored.
  • When both left and right are specified, width is ignored.
  • When using unbounded widgets (like ListView) inside Stack, you must provide explicit size constraints.
  • Positioned: Controls child positioning within a Stack
  • Align: A simpler way to align a single child
  • IndexedStack: Shows only one child from multiple children
  • CustomMultiChildLayout: For more complex layouts
  • Overlay: For displaying widgets on top of the entire app