Widget Layering and Positioning with Stack

Just as you use CSS position: absolute for absolute positioning on the web, in Flitter you can use Stack and Positioned widgets to layer widgets and position them exactly where you want.

🎯 Learning Objectives

After completing this tutorial, you’ll be able to:

  • Layer widgets using Stack
  • Specify exact positions with Positioned
  • Specify relative positions with Align
  • Understand and apply z-index concepts
  • Create practical overlay UIs

📚 Understanding the Stack Widget

Stack layers child widgets on top of each other. Widgets added later are drawn on top.

Basic Stack Structure

Stack({
  alignment: Alignment.center,  // Default alignment
  fit: StackFit.loose,         // Size adjustment method
  children: [
    // First widget (bottom layer)
    // Second widget
    // Third widget (top layer)
  ]
})

StackFit Options

  • StackFit.loose - Stack sizes to fit its children (default)
  • StackFit.expand - Stack takes up all available space from parent
  • StackFit.passthrough - Passes parent constraints to children

🎨 Understanding the Positioned Widget

Positioned specifies the exact position of a child widget within a Stack.

Positioned Properties

Positioned({
  top: 10,     // Distance from top
  right: 20,   // Distance from right
  bottom: 30,  // Distance from bottom
  left: 40,    // Distance from left
  width: 100,  // Width specification (optional)
  height: 50,  // Height specification (optional)
  child: Container({ /* ... */ })
})

🚀 Practice 1: Basic Stack Layout

Stack({
  children: [
    // Background
    Container({
      width: 300,
      height: 200,
      color: '#e3f2fd'
    }),
    
    // Center element
    Positioned({
      left: 50,
      top: 50,
      child: Container({
        width: 100,
        height: 100,
        color: '#2196f3',
        child: Center({ child: Text("Center") })
      })
    }),
    
    // Top right element
    Positioned({
      right: 10,
      top: 10,
      child: Container({
        width: 40,
        height: 40,
        color: '#f44336',
        child: Center({ child: Text("!") })
      })
    })
  ]
})

🚀 Practice 2: Creating a Profile Card

Container({
  width: 300,
  height: 180,
  child: Stack({
    children: [
      // Background image replacement
      Container({
        decoration: new BoxDecoration({
          gradient: new LinearGradient({
            colors: ['#4a90e2', '#7b68ee'],
            begin: Alignment.topCenter,
            end: Alignment.bottomCenter
          })
        })
      }),
      
      // Profile image position
      Positioned({
        left: 20,
        bottom: 20,
        child: Container({
          width: 80,
          height: 80,
          decoration: new BoxDecoration({
            color: 'white',
            shape: BoxShape.circle,
            border: Border.all({ color: 'white', width: 3 })
          }),
          child: Center({ child: Text("👤", { style: new TextStyle({ fontSize: 40 }) }) })
        })
      }),
      
      // Name and description
      Positioned({
        left: 120,
        bottom: 50,
        child: Column({
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Text("John Doe", {
              style: new TextStyle({
                color: 'white',
                fontSize: 24,
                fontWeight: 'bold'
              })
            }),
            Text("UI/UX Designer", {
              style: new TextStyle({
                color: 'rgba(255,255,255,0.8)',
                fontSize: 16
              })
            })
          ]
        })
      }),
      
      // Follow button
      Positioned({
        right: 20,
        bottom: 20,
        child: Container({
          padding: EdgeInsets.symmetric({ horizontal: 20, vertical: 10 }),
          decoration: new BoxDecoration({
            color: 'white',
            borderRadius: BorderRadius.circular(20)
          }),
          child: Text("Follow", {
            style: new TextStyle({
              color: '#4a90e2',
              fontWeight: 'bold'
            })
          })
        })
      })
    ]
  })
})

🚀 Practice 3: Icon with Notification Badge

Container({
  width: 60,
  height: 60,
  child: Stack({
    children: [
      // Icon background
      Container({
        width: 60,
        height: 60,
        decoration: new BoxDecoration({
          color: '#f5f5f5',
          shape: BoxShape.circle
        }),
        child: Center({
          child: Text("🔔", { style: new TextStyle({ fontSize: 30 }) })
        })
      }),
      
      // Notification badge
      Positioned({
        right: 0,
        top: 0,
        child: Container({
          width: 20,
          height: 20,
          decoration: new BoxDecoration({
            color: '#f44336',
            shape: BoxShape.circle,
            border: Border.all({ color: 'white', width: 2 })
          }),
          child: Center({
            child: Text("3", {
              style: new TextStyle({
                color: 'white',
                fontSize: 12,
                fontWeight: 'bold'
              })
            })
          })
        })
      })
    ]
  })
})
Container({
  width: 350,
  height: 250,
  child: Stack({
    children: [
      // Background image (replaced with color)
      Container({
        decoration: new BoxDecoration({
          gradient: new RadialGradient({
            colors: ['#ffd89b', '#19547b'],
            center: Alignment.center,
            radius: 1.5
          })
        })
      }),
      
      // Gradient overlay
      Container({
        decoration: new BoxDecoration({
          gradient: new LinearGradient({
            colors: ['transparent', 'rgba(0,0,0,0.7)'],
            begin: Alignment.topCenter,
            end: Alignment.bottomCenter,
            stops: [0.5, 1.0]
          })
        })
      }),
      
      // Title and description
      Positioned({
        left: 20,
        bottom: 20,
        right: 20,
        child: Column({
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Text("Beautiful Sunset", {
              style: new TextStyle({
                color: 'white',
                fontSize: 24,
                fontWeight: 'bold'
              })
            }),
            SizedBox({ height: 8 }),
            Text("A fantastic sunset captured at Hyeopjae Beach, Jeju Island", {
              style: new TextStyle({
                color: 'rgba(255,255,255,0.8)',
                fontSize: 14
              })
            })
          ]
        })
      }),
      
      // Like button
      Positioned({
        right: 20,
        top: 20,
        child: Container({
          width: 40,
          height: 40,
          decoration: new BoxDecoration({
            color: 'rgba(255,255,255,0.3)',
            shape: BoxShape.circle
          }),
          child: Center({
            child: Text("❤️", { style: new TextStyle({ fontSize: 20 }) })
          })
        })
      })
    ]
  })
})

🎨 Practice Exercises

  1. Floating Action Button: Create a circular button fixed to the bottom right
  2. Tooltip Overlay: Implement a tooltip that appears on hover
  3. Image Text Overlay: Place text and gradients over images

🚨 Important Notes

  1. Positioned only inside Stack: Must be a direct child of Stack
  2. Overflow handling: Be careful with elements extending outside Stack
  3. z-index: The order in the children array acts as z-index

🔗 Next Steps

💡 Key Summary

  1. Stack: Layers widgets on top of each other
  2. Positioned: Provides precise positioning within Stack
  3. z-index: Determined by order in children array
  4. StackFit: Controls how Stack sizes itself
  5. Overlay patterns: Great for badges, floating buttons, and image overlays

Stack and Positioned give you the power to create sophisticated layered UIs!