Skip to content

Stack

Stack 将子组件层叠在一起(类似 CSS 的 absolute positioning)。

构造函数

dart
Stack({
  Key? key,                                                    // 组件标识
  AlignmentGeometry alignment = AlignmentDirectional.topStart,  // 未定位子组件的对齐方式
  TextDirection? textDirection,                                  // 文本方向
  StackFit fit = StackFit.loose,                                 // 未定位子组件的尺寸约束
  Clip clipBehavior = Clip.hardEdge,                             // 裁剪行为(超出部分是否裁剪)
  List<Widget> children = const [],                              // 子组件列表(后面的绘制在上层)
})

属性速查

属性类型默认值说明
alignmentAlignmentGeometrytopStart未被 Positioned 包裹的子组件的对齐方式
textDirectionTextDirection?null文本方向(影响 start/end)
fitStackFitloose未定位子组件的尺寸约束策略
clipBehaviorCliphardEdge超出 Stack 边界的子组件是否裁剪
childrenList<Widget>[]子组件列表,后面的绘制在上层

fit 值说明

行为
StackFit.loose子组件可以从 0 到 Stack 尺寸之间自由选择(默认)
StackFit.expand子组件被强制撑满 Stack
StackFit.passthrough子组件继承 Stack 的约束

clipBehavior 值说明

行为
Clip.hardEdge硬边裁剪(默认)
Clip.antiAlias抗锯齿裁剪
Clip.none不裁剪,子组件可以溢出显示

Positioned — 精确定位

Positioned 用于在 Stack 中精确控制子组件的位置和大小。

构造函数

dart
Positioned({
  double? left,       // 距左边距离
  double? top,        // 距顶部距离
  double? right,      // 距右边距离
  double? bottom,     // 距底部距离
  double? width,      // 宽度
  double? height,     // 高度
  required Widget child,
})

属性速查

属性类型说明
leftdouble?距左边距离
topdouble?距顶部距离
rightdouble?距右边距离
bottomdouble?距底部距离
widthdouble?宽度
heightdouble?高度
childWidget子组件(必填)

定位规则

指定属性效果
top + left从左上角开始,尺寸由 child 决定
left + right水平撑满,高度由 child 决定
top + bottom垂直撑满,宽度由 child 决定
left + right + top + bottom完全撑满指定区域
Positioned.fill()四边均为 0,撑满整个 Stack

快速示例

基本层叠

dart
import 'package:flutter/material.dart';

class StackBasicExample extends StatelessWidget {
  const StackBasicExample({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Stack 基本层叠')),
      body: Center(
        // ─── ★ Stack 层叠布局 ──────────────
        child: Stack(
          children: [
            // 底层 —— 背景色
            Container(
              width: 300,
              height: 200,
              color: Colors.blue,
            ),
            // 顶层 —— 文字
            // ─── ★ Positioned 精确定位 ──────────────
            const Positioned(
              bottom: 16,
              left: 16,
              child: Text(
                '风景照片',
                style: TextStyle(
                  color: Colors.white,
                  fontSize: 20,
                  fontWeight: FontWeight.bold,
                ),
              ),
            ),
            // ─── ☆ Positioned 精确定位 ──────────────
          ],
        ),
        // ─── ☆ Stack 层叠布局 ──────────────
      ),
    );
  }
}

常用定位

dart
// 右上角
Positioned(
  top: 10,
  right: 10,
  child: const Icon(Icons.close),
)

// 底部居中
Positioned(
  bottom: 20,
  left: 0,
  right: 0,
  child: ElevatedButton(
    onPressed: () {},
    child: const Text('操作'),
  ),
)

// 填充整个区域
Positioned.fill(
  child: Container(color: Colors.black.withOpacity(0.5)),
)

// 左半边
Positioned(
  left: 0,
  top: 0,
  bottom: 0,
  width: 150,
  child: Container(color: Colors.blue.withOpacity(0.3)),
)

未定位子组件对齐

dart
Stack(
  alignment: Alignment.center,  // 未定位子组件居中
  children: [
    Container(width: 200, height: 200, color: Colors.blue),
    const Text('我居中'),  // 没有用 Positioned 包裹
  ],
)

常见模式

图片上叠加渐变 + 文字

dart
SizedBox(
  width: 300,
  height: 200,
  child: Stack(
    children: [
      // 背景图
      Image.network(url, fit: BoxFit.cover, width: 300, height: 200),
      // 底部渐变遮罩
      Positioned.fill(
        child: DecoratedBox(
          decoration: BoxDecoration(
            gradient: LinearGradient(
              begin: Alignment.bottomCenter,
              end: Alignment.topCenter,
              colors: [Colors.black.withOpacity(0.7), Colors.transparent],
            ),
          ),
        ),
      ),
      // 底部文字
      Positioned(
        bottom: 16,
        left: 16,
        right: 16,
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: const [
            Text('标题', style: TextStyle(color: Colors.white, fontSize: 18, fontWeight: FontWeight.bold)),
            SizedBox(height: 4),
            Text('副标题', style: TextStyle(color: Colors.white70, fontSize: 14)),
          ],
        ),
      ),
    ],
  ),
)

头像 + 在线状态指示器

dart
SizedBox(
  width: 60,
  height: 60,
  child: Stack(
    children: [
      const CircleAvatar(
        backgroundImage: NetworkImage(avatarUrl),
        radius: 28,
      ),
      Positioned(
        bottom: 0,
        right: 0,
        child: Container(
          width: 16,
          height: 16,
          decoration: BoxDecoration(
            color: Colors.green,
            shape: BoxShape.circle,
            border: Border.all(color: Colors.white, width: 2),
          ),
        ),
      ),
    ],
  ),
)

加载中遮罩

dart
Stack(
  children: [
    // 原有内容
    ListView(children: [...]),
    // 加载遮罩
    if (_isLoading)
      Positioned.fill(
        child: Container(
          color: Colors.black.withOpacity(0.3),
          child: const Center(
            child: CircularProgressIndicator(),
          ),
        ),
      ),
  ],
)

IndexedStack

IndexedStack 是 Stack 的变体——只显示一个子组件,但保持所有子组件的状态。

构造函数

dart
IndexedStack({
  Key? key,                                    // 组件标识
  int index = 0,                                // 当前显示的子组件索引
  AlignmentGeometry alignment = AlignmentDirectional.topStart, // 对齐方式
  TextDirection? textDirection,                  // 文本方向
  StackFit sizing = StackFit.loose,              // 尺寸约束
  List<Widget> children = const [],              // 子组件列表
})
dart
IndexedStack(
  index: _currentIndex,  // 显示第几个子组件
  children: [
    const HomeTab(),
    const SearchTab(),
    const ProfileTab(),
  ],
)

使用场景

适用于底部导航栏切换 Tab 时保持各 Tab 状态(如滚动位置、输入框内容不被丢失)。

Stack vs Column/Row

特性StackColumnRow
排列方式层叠垂直排列水平排列
子组件是否重叠可以重叠不重叠不重叠
定位方式Positioned 或 alignment主轴/交叉轴对齐主轴/交叉轴对齐

基于 Flutter 官方文档整理