[Flutter 基础] - Flutter核心布局组件 - Stack -意想不到的效果

548 阅读4分钟

在Flutter中, Stack是核心布局组件之一,主要用于叠加布局实现有一些复杂的页面效果,悬浮按钮、蒙层、徽章提示等,熟练掌握Stack基本属性,并结合其它组件灵活使用可以完成非常炫酷的页面绘制。 以下是stack组件的一些基本属性和用法介绍。


一、基本概念

Stack 允许子组件以层叠的方式排列(类似 CSS 的绝对定位),常用于叠加元素(如图标上的徽章、对话框、悬浮按钮等)。默认情况下,子组件会从左上角开始堆叠。

image.png

这应该是我们裁切图片时经常见到的一个效果。 这个示例就是通过三层层叠的效果实现的,底部是一张图片,中间是一个蒙版,最上层是一个被裁切过的图片,


二、核心属性

1. 对齐方式:alignment

控制所有子组件的默认对齐方式(未使用 Positioned 包裹的子组件):

常见值
  • Alignment.topLeft (默认值)

image.png

  • Alignment.topCenter

image.png

  • Alignment.topRight

image.png

  • Alignment.centerLeft

image.png

  • Alignment.center

image.png

  • Alignment.centerRight

image.png

  • Alignment.bottomLeft

image.png

  • Alignment.bottomCenter

image.png

  • Alignment.bottomRight

image.png

  • Alignment(0.5, 0.5)(自定义偏移)

image.png

2. Alignment(double x, double y)偏移量计算

image.png

是一个y轴从上到下,x轴从左至右计算的方式;最大值是(1,1)左下角,最小值是(-1,-1)左上角,中间是(0,0)

  • Alignment(-1, -1)

image.png

  • Alignment(0, 0)

image.png

-Alignment(1, 1)

image.png

2. 尺寸适配:fit

控制非定位子组件(未被 Positioned 包裹的组件)如何适应 Stack 的尺寸:

  • StackFit.loose(默认):子组件不受约束,按自身大小显示
  • StackFit.expand:强制子组件填满 Stack 的可用空间

image.png

Container(
  alignment: Alignment.center,
  width: 300,
  height: 400,
  child: Stack(
    alignment: Alignment(-1, -1),
    fit: StackFit.expand,
    children: <Widget>[
      Container(width: 200, height: 300, color: Colors.red), //被迫和父级Container一样大,并且背遮挡住了
      Positioned(
        child: Container(width: 100, height: 100, color: Colors.green),// 被迫和父级Container一样大
      ),
    ],
  ),
),
  • StackFit.passthrough:继承父容器的约束

image.png

Container(
  alignment: Alignment.center,
  width: 300,
  height: 400,
  child: Stack(
    alignment: Alignment(-1, -1),
    fit: StackFit.passthrough,
    children: <Widget>[
      Container(width: 200, height: 300, color: Colors.red),
      Positioned(
        child: Container(width: 100, height: 100, color: Colors.green),
      ),
    ],
  ),
),

3. 裁剪行为:clipBehavior

控制子组件溢出 Stack 时的裁剪方式:

  • Clip.hardEdge(默认):快速裁剪,不抗锯齿
  • Clip.antiAlias:平滑裁剪(性能略低)
  • Clip.none:不裁剪(可能导致溢出)
Stack(
  clipBehavior: Clip.none, // 允许子组件超出Stack范围
  children: [Positioned(top: -20, child: Icon(Icons.star))],
)

三、动态定位子组件

通过 Positioned 包裹子组件,实现精确的位置控制:

1. 基础定位

Stack(
  children: [
    Positioned(
      left: 10,
      top: 20,
      child: Icon(Icons.notifications),
    ),
    Positioned(
      right: 0,
      bottom: 0,
      child: Text("End"),
    ),
  ],
)

2. 相对尺寸

Positioned.fill( // 填满父容器
  child: Container(color: Colors.black12),
);

Positioned(
  left: 10,
  right: 10, // 左右边距各10
  height: 50,
  child: Container(color: Colors.red),
);

四、常见布局场景

1. 头像叠加徽章

image.png

Stack(
  clipBehavior: Clip.none, // 允许徽章超出Stack
  children: [
    CircleAvatar(radius: 30),
    Positioned(
      right: -5,
      bottom: -5,
      child: Container(
        padding: EdgeInsets.all(2),
        decoration: BoxDecoration(
          color: Colors.red,
          shape: BoxShape.circle,
        ),
        child: Text("3", style: TextStyle(color: Colors.white)),
      ),
  ],
)

2. 背景虚化,局部高亮

image.png

Stack(
  alignment: Alignment(0, 0),
  fit: StackFit.passthrough,
  children: <Widget>[
    Image.asset(
      'assets/image/33333.jpeg', // 图片的本地地址
      width: 300,
      height: 400,
      fit: BoxFit.none, // 图片填充方式
    ),
    Center(
      child: ClipRect(
        child: BackdropFilter(
          filter: ImageFilter.blur(sigmaX: 2.0, sigmaY: 2.0),
          child: Container(
            width: 300,
            height: 400,
            decoration: BoxDecoration(color: Colors.grey.shade200.withOpacity(0.5)),
          ),
        ),
      ),
    ),
    SizedBox(
      width: 200,
      height: 200,
      child: ClipOval(
        child: Image.asset(
          'assets/image/33333.jpeg', // 图片的本地地址
          fit: BoxFit.none,
          width: 300,
          height: 400,
        ),
      )
    ),
  ],
),

2. 底部导航栏悬浮按钮

Stack(
  alignment: Alignment.bottomCenter,
  children: [
    Scaffold(body: ...), // 页面内容
    Positioned(
      bottom: 30,
      child: FloatingActionButton(
        onPressed: () {},
        child: Icon(Icons.add),
      ),
    ),
  ],
)

五、注意事项

  1. 子组件默认左上对齐:未使用 PositionedAlign 的子组件会堆叠在左上角。
  2. 尺寸问题:如果 Stack 的父容器未提供约束(如直接放在 ListView 中),需显式设置尺寸(如 SizedBox)。
  3. 性能优化:避免在 Stack 中嵌套过多子组件,尤其是需要动态更新的组件。
  4. 组合使用:常与 PositionedAlignTransform 等组件配合实现复杂效果。

六、总结

Stack 是 Flutter 中实现层叠布局的核心组件,通过 PositionedAlign 可灵活控制子组件的位置和层级。适用于需要元素叠加的场景(如悬浮按钮、蒙层、徽章提示等),但需注意处理溢出和尺寸约束问题。