层叠布局Stack

  1. 基本作用
  2. Stack的定义
  3. 基本语法
  4. Stack的fit
  5. Positioned绝对定位

基本作用

Stack 会把多个子组件 叠放在一起(Z 轴方向的布局),和 Row / Column 不同的是:

  • Row / Column 是沿 X / Y 轴排列
  • Stack 是沿 Z 轴 叠放

以把 Stack 想象成 Photoshop 里的图层系统,后添加的元素会叠在前面的上面。

在 Flutter 的 Stack 中,默认的排列(叠放)顺序就是按照 children 列表里的代码顺序来决定的:

  • 前面写的子组件(列表中靠前的)会先绘制,所以它会在下面
  • 后面写的子组件会后绘制,所以它会盖在上面

Stack的定义

Stack({

    Key key,
    
    // 对齐方式,默认是左上角(topStart)
  	// 这里的对齐的位置是相对 Stack 这个容器内部来说的
    this.alignment = AlignmentDirectional.topStart,
    
    // 对齐方向
    this.textDirection,
    
    // 定义如何设置无定位子元素尺寸,默认为loose
    this.fit = StackFit.loose,
  
  // 对超出 Stack 显示空间的部分如何剪裁
  this.clipBehavior = Clip.hardEdge,
    
    // 子元素
    List<Widget> children = const <Widget>[],

})

基本语法

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Stack(
        // 居中对齐
        alignment: Alignment.center,
        // 子元素层叠放
        children: [
          // 三个色块
          Container(width: 100, height: 100, color: Colors.green),
          Container(width: 300, height: 300, color: Colors.amber),
          Container(width: 200, height: 200, color: Colors.blue),
        ],
      ),
    );
  }
}

由于绿色色块更靠前,所有先绘制,又由于绿色色块小,会被红色色块盖住,所有不会显示。

Stack的大小默认由最大子元素(300×300 的那个容器)决定。

这是因为Stack默认使用的是StackFit.loose,是一个松约束。那么Stack的大小就有最大的子组件决定。

const Stack({
  super.key,
  this.alignment = AlignmentDirectional.topStart,
  this.textDirection,
  this.fit = StackFit.loose,
  this.clipBehavior = Clip.hardEdge,
  super.children,
});

Stackfit

fit 决定了 Stack 自身在父容器内如何确定大小,影响 非定位子元素 的布局。

含义 说明
StackFit.loose 松约束 Stack 尽可能包裹非定位子元素的最大尺寸(默认)。子元素不会被强制拉伸。
StackFit.expand 紧约束 Stack 尽可能填满父容器,非定位子元素会收到 父约束,容易被拉伸填满。
StackFit.passthrough 透传约束 Stack 大小 = 父约束大小,但不强制拉伸非定位子元素,约束传递给子元素时不修改。

如果我们把上面的那个案例中的fit改成StackFit.expand,那么最终是蓝色会占据整个屏幕

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Stack(
        // 全屏
        fit: StackFit.expand,
        // 居中对齐
        alignment: Alignment.center,
        // 子元素层叠放
        children: [
          // 三个色块
          Container(width: 100, height: 100, color: Colors.green),
          Container(width: 300, height: 300, color: Colors.amber),
          Container(width: 200, height: 200, color: Colors.blue),
        ],
      ),
    );
  }
}

这是因为StackFit.expand设置了一个紧约束,把Stack的大小拉伸到整个屏幕的大小:

同时由于父组件的紧约束的作用,Container的大小也被拉伸到屏幕大小:

如果需要实现Container不被拉伸,中间还需要放一个组件,来隔绝:


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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Stack(
        // 全屏
        fit: StackFit.expand,
        // 居中对齐
        alignment: Alignment.center,
        // 子元素层叠放
        children: [
          // 三个色块
          Align(
            alignment: Alignment.center,
            child: Container(width: 100, height: 100, color: Colors.green),
          ),
          Align(
            alignment: Alignment.center,
            child: Container(width: 300, height: 300, color: Colors.amber),
          ),
          Align(
            alignment: Alignment.center,
            child: Container(width: 200, height: 200, color: Colors.blue),
          ),
        ],
      ),
    );
  }
}

Align是一个松约束,可以让Container保持自身设计的大小。

Positioned绝对定位

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Stack(
        // 居中对齐
        alignment: Alignment.center,
        // 子元素溢出, none 不裁剪
        clipBehavior: Clip.none,
        // 子元素层叠放
        children: [
          // 三个色块
          Container(width: 100, height: 100, color: Colors.green),
          Container(width: 300, height: 300, color: Colors.amber),
          Container(width: 200, height: 200, color: Colors.blue),

          // 绝对定位
          const Positioned(left: 0, bottom: -50, child: FlutterLogo(size: 100)),
        ],
      ),
    );
  }
}

clipBehavior:决定超出 Stack 范围的内容是否裁剪:

  • Clip.none:不裁剪
  • Clip.hardEdge / Clip.antiAlias:裁剪掉溢出的部分

Positioned 是用来给子组件绝对定位的,可以用 left/right/top/bottom 任意组合控制位置。

const Positioned({

    Key key,
    
    this.left, // 上下左右位置
    this.top,
    this.right,
    this.bottom,
    
    this.width, // 宽高
    this.height,
    
    @required Widget child,
  
})

×

喜欢就点赞,疼爱就打赏