Overview

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

Positioned allows you to place child widgets absolutely by specifying distances from the edges of the Stack. Similar to CSS’s position: absolute, it uses top, bottom, left, and right properties to control positioning. Positioned must be a direct child of a Stack widget.

When to use it?

  • When fixing widgets at specific positions within a Stack
  • When creating overlays or floating elements
  • When overlaying badges or notifications on other widgets
  • When building custom layouts
  • When placing UI elements like FABs (Floating Action Buttons)

Basic Usage

Stack({
  children: [
    // Background widget
    Container({ color: 'lightgray' }),
    
    // Positioned at top-left
    Positioned({
      top: 10,
      left: 10,
      child: Text('Top Left')
    }),
    
    // Positioned at bottom-right
    Positioned({
      bottom: 10,
      right: 10,
      child: Text('Bottom Right')
    })
  ]
})

Props

Positioning Props

top

Value: number | undefined

Distance from the top edge of the Stack in pixels.

Positioned({
  top: 20,
  child: Text('20 pixels from top')
})

bottom

Value: number | undefined

Distance from the bottom edge of the Stack in pixels.

Positioned({
  bottom: 20,
  child: Text('20 pixels from bottom')
})

left

Value: number | undefined

Distance from the left edge of the Stack in pixels.

Positioned({
  left: 20,
  child: Text('20 pixels from left')
})

Value: number | undefined

Distance from the right edge of the Stack in pixels.

Positioned({
  right: 20,
  child: Text('20 pixels from right')
})

Sizing Props

width

Value: number | undefined

Explicitly sets the width of the child widget. Ignored if both left and right are specified.

Positioned({
  top: 10,
  left: 10,
  width: 100,
  height: 50,
  child: Container({ color: 'blue' })
})

height

Value: number | undefined

Explicitly sets the height of the child widget. Ignored if both top and bottom are specified.

Positioned({
  top: 10,
  left: 10,
  width: 100,
  height: 50,
  child: Container({ color: 'red' })
})

child (required)

Value: Widget

The child widget to be positioned.

Positioned({
  top: 50,
  left: 50,
  child: Icon({ icon: Icons.star, color: 'yellow' })
})

Static Methods

Positioned.fill()

Creates a Positioned widget that fills the entire Stack. It has 0 distance from all edges.

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

// Equivalent to:
Positioned({
  top: 0,
  bottom: 0,
  left: 0,
  right: 0,
  child: Container({ color: 'rgba(0, 0, 0, 0.5)' })
})

Position and Size Combinations

Sizing Through Opposing Constraints

When both left and right are specified, width is automatically calculated. When both top and bottom are specified, height is automatically calculated.

// Width is automatically calculated (Stack width - 40)
Positioned({
  top: 10,
  left: 20,
  right: 20,
  child: Container({ height: 50, color: 'green' })
})

// Height is automatically calculated (Stack height - 40)
Positioned({
  top: 20,
  bottom: 20,
  left: 10,
  child: Container({ width: 50, color: 'blue' })
})

Practical Examples

Example 1: Badge Display

const notificationIcon = Container({
  width: 50,
  height: 50,
  child: Stack({
    children: [
      Icon({ icon: Icons.notifications, size: 40 }),
      Positioned({
        top: 0,
        right: 0,
        child: Container({
          width: 20,
          height: 20,
          decoration: BoxDecoration({
            shape: 'circle',
            color: 'red'
          }),
          alignment: Alignment.center,
          child: Text('3', {
            style: TextStyle({ color: 'white', fontSize: 12 })
          })
        })
      })
    ]
  })
});

Example 2: Text Overlay on Image

const imageWithOverlay = Container({
  width: 300,
  height: 200,
  child: Stack({
    fit: StackFit.expand,
    children: [
      Image({ src: 'landscape.jpg', fit: 'cover' }),
      // Bottom gradient
      Positioned({
        bottom: 0,
        left: 0,
        right: 0,
        height: 80,
        child: Container({
          decoration: BoxDecoration({
            gradient: LinearGradient({
              begin: Alignment.bottomCenter,
              end: Alignment.topCenter,
              colors: ['rgba(0,0,0,0.8)', 'transparent']
            })
          })
        })
      }),
      // Text
      Positioned({
        bottom: 20,
        left: 20,
        right: 20,
        child: Column({
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Text('Beautiful Landscape', {
              style: TextStyle({
                color: 'white',
                fontSize: 24,
                fontWeight: 'bold'
              })
            }),
            Text('Photo by John Doe', {
              style: TextStyle({
                color: 'rgba(255,255,255,0.8)',
                fontSize: 14
              })
            })
          ]
        })
      })
    ]
  })
});

Example 3: Floating Action Button

const screenWithFAB = Scaffold({
  body: Stack({
    children: [
      // Main content
      ListView({
        children: List.generate(20, (i) => 
          ListTile({ title: Text(`Item ${i + 1}`) })
        )
      }),
      // FAB
      Positioned({
        bottom: 16,
        right: 16,
        child: FloatingActionButton({
          onPressed: () => console.log('FAB clicked'),
          child: Icon({ icon: Icons.add })
        })
      })
    ]
  })
});

Example 4: Tooltip Display

const tooltipExample = Stack({
  clipped: false, // Allow tooltip to extend beyond Stack bounds
  children: [
    ElevatedButton({
      onPressed: () => {},
      child: Text('Hover me')
    }),
    if (showTooltip) Positioned({
      bottom: 40,
      left: -20,
      child: Container({
        padding: EdgeInsets.all(8),
        decoration: BoxDecoration({
          color: 'black',
          borderRadius: BorderRadius.circular(4)
        }),
        child: Text('This is a tooltip', {
          style: TextStyle({ color: 'white' })
        })
      })
    })
  ]
});

Important Notes

  • Positioned must be a direct child of Stack.
  • When both top and bottom are specified, height is ignored.
  • When both left and right are specified, width is ignored.
  • At least one positioning property (top, bottom, left, right) must be specified.
  • If the Stack’s size is not determined, Positioned widget placement may be unexpected.
  • Positioned widgets don’t contribute to the Stack’s size calculation.
  • Stack: The parent widget where Positioned is used
  • Align: A simpler way to align a single child
  • Container: Simple alignment possible with alignment property
  • Transform: Positioning through transformation instead of position
  • CustomMultiChildLayout: For more complex layout requirements