写点什么

flutter 系列之:flutter 中常用的 Stack layout 详解

作者:程序那些事
  • 2022 年 6 月 14 日
  • 本文字数:2958 字

    阅读完需:约 10 分钟

简介

对于现代 APP 的应用来说,为了更加美观,通常会需要用到不同图像的堆叠效果,比如在一个 APP 用户背景头像上面添加一个按钮,表示可以修改用户信息等。


要实现这样的效果,我们需要在一个 Image 上面堆叠其他的 widget 对象,flutter 为我们提供了这样的一个非常方便的 layout 组件叫做 Stack,今天和大家一起来聊一聊 Stack 的使用。

Stack 详解

我们先来看下 Stack 的定义:


class Stack extends MultiChildRenderObjectWidget 
复制代码


Stack 继承自 MultiChildRenderObjectWidget,表示在 stack 中可以 render 多个 child widget 对象。


因为 Stack 中的 child 是重叠关系,所以需要对 child 进行定位,根据定位的不同 Stack 中的 child 可以分为两种类型,分别是 positioned 和 non-positioned。


所谓 positioned,是指 child widget 被包装在 Positioned 中。什么是 Positioned 呢?


Positioned 是专门用来定位 Stack 中的 child 位置的一个 widget。所以 Positioned 必须用在 Stack 中,并且 Positioned 和 Stack 的路径之间只能存在 StatelessWidget 或者 StatefulWidget 这两种 widget。


如果一个对象被包含在 Positioned 中,那么这个对象就是一个 Stack 中的 positioned 对象。


Positioned 中除了封装的 child 之外,还有 6 个属性,如下所示:


  const Positioned({    Key? key,    this.left,    this.top,    this.right,    this.bottom,    this.width,    this.height,    required Widget child,  })
复制代码


这六个属性分别是 left,top,right,bottom,width 和 height。其中 left,top,right,bottom 分别表示到左,顶,右,底的距离,这个距离是相对 stack 来说的。而 width 和 height 则表示的是 Positioned 的宽度和高度。


事实上,使用 left 和 right 可以定义出 width,使用 top 和 bottom 可以定义出 height。


如果在一个轴方向的三个值都不存在,那么会使用 Stack.alignment 来定位子元素。


如果六个值都不存在,那么这个 child 就是一个 non-positioned 的 child。


对于 non-positioned 的 child,是通过 Stack 的 alignment 来进行布局的,默认情况下是按 top left corners 进行布局的。

Stack 的属性

我们接下来看一下 Stack 中有哪些属性,下面是 Stack 的构造函数:


  Stack({    Key? key,    this.alignment = AlignmentDirectional.topStart,    this.textDirection,    this.fit = StackFit.loose,    @Deprecated(      'Use clipBehavior instead. See the migration guide in flutter.dev/go/clip-behavior. '      'This feature was deprecated after v1.22.0-12.0.pre.',    )    this.overflow = Overflow.clip,    this.clipBehavior = Clip.hardEdge,    List<Widget> children = const <Widget>[],  })
复制代码


可以看到 Stack 中有 alignment,textDirection,fit,overflow 和 clipBehavior 这几个属性。


首先来看 alignment,这里的 alignment 是一个 AlignmentGeometry 对象,主要用来布局 non-positioned children。


AlignmentGeometry 中有两个需要设置的属性,分别是 start 和 y。


start 表示的是横线定位范围,它的取值比较奇怪,-1 表示的是 start side 的边缘位置,而 1 表示的是 end side 的边缘位置。如果取值超过了这个范围,则表示对应的位置超过了边缘位置。


start 的位置跟 TextDirection 是相关联的,如果 TextDirection 的值是 ltr,也就是说从左到右排列,那么 start 就在最左边,如果 TextDirection 的值是 rtl,也就是说从右到左排列,那么 start 就是在最右边。


有横向位置就有纵向位置,这个纵向位置用 y 来表示,它的正常取值范围也是-1 到 1,当然你也可以超出这个范围。


为了用户更加方便的使用 AlignmentGeometry,AlignmentGeometry 提供了一些便捷的方法,如 topStart,topCenter,topEnd 等,大家可以自行选取。


接下来的属性是 textDirection,textDirection 是一个 TextDirection 对象,它有两个值,分别是 rtl 和 ltr,在讲解 alignment 的时候,我们已经提到过 textDirection,它会影响 alignment 中横向的布局。


接下来是 StackFit 类型的 fit 属性,StackFit 有三个值,分别是 loose,expand 和 passthrough。


loose 表示的是一个松散结构,比如 Stack 规定的 size 是 300x500,那么它的 child 的宽度可以从 0-300,child 的高度可以从 0-500.


expand 表示是一个扩充的效果,比如 Stack 规定的 size 是 300x500,那么它的 child 的宽度就是 300,child 的高度就是 500.


passthrough 表示传递给 stack 的限制会原封不动的传递给他的 child,不会进行任何修改。


overflow 表示 children 超出展示部分是否会被剪切。不过这个属性已经是 Deprecated,flutter 推荐我们使用 clipBehavior 这个属性来代替。


clipBehavior 是一个 Clip 对象,它的默认值是 Clip.hardEdge。其他的几个值还有 none,hardEdge,antiAlias 和 antiAliasWithSaveLayer。


none 表示不进行任何裁剪,hardEdge 的裁剪速度最快,但是精确度不高。antiAlias 速度比 hardEdge 慢一点,但是有光滑的边缘。antiAliasWithSaveLayer 是最慢的,应该很少被使用。

Stack 的使用

有了上面的讲解,接下来我们看一下 Stack 的具体使用。


在我们这个例子中,我们在 Stack 中设置一个背景图片,然后在图片上叠加一个文本。


那么应该怎么实现呢?


首先我们需要设置 Stack 的 alignment 方式,我们希望文本和图片的中心重合,也就是说把文字放在图片中间,我们将 Stack 的 alignment 设置为 Alignment.center。


接下来是一个背景图片,因为原始图片是一个正方形的图片,我们需要对图片进行裁剪成圆形,这里使用一个非常方便的类 CircleAvatar 来创建圆形的图标:


 const CircleAvatar(          backgroundImage: AssetImage('images/head.jpg'),          radius: 100,        ),
复制代码


上面的代码能够创建一个半径是 100 的圆。


然后是文本的创建,可以给 Text 设置文本内容和对应的 style:


Text(            '编辑',            style: TextStyle(              fontSize: 20,              fontWeight: FontWeight.bold,              color: Colors.white,            ),          )
复制代码


然后把 Text 封装在 Container 中,并使用 BoxDecoration 给他指定一个背景:


Container(          decoration: const BoxDecoration(            color: Colors.green,          ),          child: const Text(              ...
复制代码


最后将上面的代码组合起来就是我们最后的 Stack:


 Widget build(BuildContext context) {    return Stack(      alignment: Alignment.center,      children: [        const CircleAvatar(          backgroundImage: AssetImage('images/head.jpg'),          radius: 100,        ),        Container(          decoration: const BoxDecoration(            color: Colors.green,          ),          child: const Text(            '编辑',            style: TextStyle(              fontSize: 20,              fontWeight: FontWeight.bold,              color: Colors.white,            ),          ),        ),      ],    );
复制代码


运行生成的界面如下:


总结

以上就是 Stack 的使用,通过堆叠组件,我们可以实现很多炫酷的功能。


本文的例子:https://github.com/ddean2009/learn-flutter.git


更多内容请参考 http://www.flydean.com/11-flutter-ui-layout-stack/

最通俗的解读,最深刻的干货,最简洁的教程,众多你不知道的小技巧等你来发现!

欢迎关注我的公众号:「程序那些事」,懂技术,更懂你!

发布于: 刚刚阅读数: 4
用户头像

关注公众号:程序那些事,更多精彩等着你! 2020.06.07 加入

最通俗的解读,最深刻的干货,最简洁的教程,众多你不知道的小技巧,尽在公众号:程序那些事!

评论

发布
暂无评论
flutter系列之:flutter中常用的Stack layout详解_flutter_程序那些事_InfoQ写作社区