# 【Demo】Stack + Positioned 层叠布局 > 源文件:`lib/pages/demo/stack_demo.dart` | 分类:基础布局 > 一句话:UI 重叠 = CSS 的 `position: absolute`,本页附赠一个真实踩过的"全 Positioned 尺寸崩溃"大坑。 --- ## 一、这个 Demo 解决什么问题 做"头像 + 右上角角标"、"卡片 + 半透明蒙层"、"悬浮按钮"这类**元素重叠**的 UI。 Flutter 用 `Stack` 实现: - `Stack` = 图层容器(**后写的子组件盖在上面**); - `Positioned` = 给子组件做绝对定位(**只能直接放在 Stack 的 children 里**)。 --- ## 二、怎么运行 / 演示内容 ```dart runApp(const MaterialApp(home: StackDemoPage())); ``` 页面演示 5 块内容: 1. 基础重叠:蓝色盒子 + 右上角悬空角标 + 居中文字; 2. `Positioned.fill` 半透明蒙层; 3. 坑①复现:全 Positioned 时 Stack 尺寸异常; 4. 修复方案 1:SizedBox 固定宽高; 5. 修复方案 2:非 Positioned 底层组件撑尺寸。 --- ## 三、核心知识点(新手向) ### 1. Stack 的两个核心参数 ```dart Stack( alignment: Alignment.topLeft, // 对【没被 Positioned 包裹】的子组件做默认对齐 clipBehavior: Clip.none, // 允许子组件绘制超出 Stack 边界(默认 Clip.hardEdge 会裁剪) children: [...], ) ``` ### 2. Positioned 的用法 ```dart Positioned( top: 10, // 距 Stack 上边 right: -10, // 负值 = 超出边界(配合 clipBehavior: Clip.none 才能显示) child: ..., ) Positioned.fill( // 填满整个 Stack —— 做蒙层最方便 child: Container(color: Colors.black54), ) ``` ### 3. 布局规则一句话 Stack 的尺寸 = **非 Positioned 子组件**决定的(取其中最大的那个); Positioned 组件只是"贴"在上面,不参与 Stack 自身尺寸计算。 --- ## 四、关键代码剖析(含大坑) ### 坑①:全部子组件都是 Positioned → Stack 尺寸异常 ```dart // 真实行为:Stack 尺寸不由内容决定,直接取父约束最大值(constraints.biggest) Stack( children: [ Positioned(top: 10, left: 10, child: Text("A")), ], ) ``` - 放在**有界约束**里(如 SizedBox 300×60)→ Stack 占满整个 300×60,而不是收缩到文字大小; - 裸放在 **Column 等无界高度**里 → debug 模式直接断言崩溃: `A Stack requires bounded constraints`,**整个 body 都画不出来**(只看到标题栏)。 ### 修复方案 1:给 Stack 套 SizedBox 固定宽高 ```dart SizedBox( width: 300.w, height: 80.h, child: Stack(children: [Positioned(top: 10, left: 10, child: Text("A"))]), ) ``` ### 修复方案 2:放一个非 Positioned 的底层组件撑起大小 ```dart Stack( children: [ ColoredBox( // 非 Positioned 底层组件:由它决定 Stack 尺寸 color: Color(0xFFE8E8E8), child: SizedBox(width: 300.w, height: 80.h), ), Positioned(top: 10, left: 10, child: Text("A")), ], ) ``` > 经验:**Stack 里永远留一个"地基"**(非 Positioned 的底层组件或外层 SizedBox), > 既避免崩溃,也方便后续加 Positioned 不担心尺寸突变。 --- ## 五、踩坑记录 1. **Positioned 只能直接作为 Stack 的 children**,中间套了其他 Widget 会失效(编译警告)。 2. **做悬浮角标务必 `clipBehavior: Clip.none`**,否则负偏移部分被裁掉看不见。 3. **全 Positioned 的 Stack 在无界约束下崩溃**(见上),这是本页作者真实遇到过的 bug:页面只剩标题。 4. `Container` 在 Flutter 3.44 已不是 const 构造,外层 `Center` 别写 `const`(编译报错)。 5. 想让内容很短也能拉动滚动(演示页效果),用 `physics: BouncingScrollPhysics()` / `AlwaysScrollableScrollPhysics()`。 --- ## 六、扩展思考 - 蒙层进阶:`Positioned.fill` + 半透明色 + 点击手势 = 简易弹窗/引导层; - 角标进阶:红点角标(Badge 组件)本质也是 Stack + Positioned 的封装; - 与 [list_demo.md](list_demo.md) 对照:列表右下角的"回到顶部"FAB 就是 `Stack + Positioned` 实现的; - 与 [smooth_sheet_demo.md](smooth_sheet_demo.md) 对照:地图背景 + 底部面板就是"Stack 背景层 + 面板层"的组合。