这是我参与更文挑战的第12天,活动详情查看: 更文挑战html
前面几篇介绍了ListView
、GridView
、Wrap
、Column
、Row
等这些在平面列表布局,今天咱们要聊的是 Stack
,能够叫他栈布局
也叫层级布局
,主要来显示平面重叠布局,能够更加灵活的控制子项的位置。git
这里咱们添加 3 个大小不一样子项,看看他们是怎么排列的。github
Stack(
children: [
getItem(3, width: 120, height: 120, color: Colors.purple),
getItem(2, width: 80, height: 80, color: Colors.blue),
getItem(1),
],
)
复制代码
这里除 index 外,其余参数使用了选择参数api
/// 获取子项目(这里使用了选择参数)
Widget getItem(int index,
{double? width = 60, double? height = 60, Color color = Colors.orange}) {
return Container(
// 宽高设置 60
width: width,
height: height,
// 设置背景色
color: color,
// 设置间隙
margin: EdgeInsets.all(2),
// 设置子项居中
alignment: Alignment.center,
// 设置子项
child: Text('$index'),
);
}
复制代码
若是你看看源码能够看到这个参数的默认值是 AlignmentDirectional.topStart
,可是这里咱们依然可使用咱们熟悉的 Alignment.topLeft
来进行参数设置,这是为何呢?咱们看看来看看 AlignmentDirectional
源码吧,Alignment
源码在前面的章节咱们看过了markdown
Stack(
// 居中对齐
alignment: Alignment.topLeft,
children: [
getItem(3, width: 120, height: 120, color: Colors.purple),
getItem(2, width: 80, height: 80, color: Colors.blue),
getItem(1),
],
)
复制代码
// Alignment
class Alignment extends AlignmentGeometry {
/// Creates an alignment.
///
/// The [x] and [y] arguments must not be null.
const Alignment(this.x, this.y)
: assert(x != null),
assert(y != null);
static const Alignment topLeft = Alignment(-1.0, -1.0);
static const Alignment center = Alignment(0.0, 0.0);
...
}
// AlignmentDirectional
class AlignmentDirectional extends AlignmentGeometry {
const AlignmentDirectional(this.start, this.y)
: assert(start != null),
assert(y != null);
static const AlignmentDirectional topStart = AlignmentDirectional(-1.0, -1.0);
// 考虑使用 Alignment.center 代替
static const AlignmentDirectional center = AlignmentDirectional(0.0, 0.0);
...
}
复制代码
上面咱们贴出了核心源码,能够看出他俩都是继承自 AlignmentGeometry
,而后实现也是差很少,并且源码中的注释也是推荐咱们使用 Alignment 代替oop
这里咱们就使用 Alignment 了源码分析
Alignment.topLeft | Alignment.topCenter | Alignment.topRight |
---|---|---|
![]() |
![]() |
![]() |
Alignment.centerLeft | Alignment.center | Alignment.centerRight |
![]() |
![]() |
![]() |
Alignment.bottomLeft | Alignment.bottomCenter | Alignment.bottomRight |
![]() |
![]() |
![]() |
当咱们输入 **Alignment.bottomRight**
这类参数时,由于单次比较长若是所有输入就太麻烦了,咱们能够输入部分前缀+常量的首字母,以下布局
阅读我得文章你会发现,不光有源码分析、结构化梳理还有各类实用的技巧,记得关注我哦post
为了展现效果,咱们先添加一个背景 BgContainer
(以前的篇章封装的通用组件)ui
BgContainer(
child: Stack(
// 居中
alignment: Alignment.center,
// 设置默认值 loose
fit: StackFit.loose,
children: [
getItem(3, width: 120, height: 120, color: Colors.purple),
getItem(2, width: 80, height: 80, color: Colors.blue),
getItem(1),
],
),
)
复制代码
这里为了显示效果咱们使用了 DevTools 中的 Widget Inspector
来调试布局,以后可能会聊 Flutter DevTools 的使用技巧(这里不作承诺,仍是可能回聊)
经过上图能够看到此时 Stack 的大小取决于子项中最大的(也就是紫色 120
宽高的子项)
这里彷佛不是很符合预期,理论上不该该是扩展 Stack 到最大便可吗?
目前咱们的子项都是没有设置定位的,因此此时全部的子类约束都会扩展到与Stack 最大值一致
咱们此时作一点点改变看看效果,先看代码
BgContainer(
child: Stack(
alignment: Alignment.center,
fit: StackFit.expand,
children: [
getItem(3, width: 120, height: 120, color: Colors.purple),
getItem(2, width: 80, height: 80, color: Colors.blue),
// 这添加了定位
Positioned(
// 距左边 10
left: 10,
// 距上边 10
top: 10,
child: getItem(1),
),
],
),
)
复制代码
此时看到效果了,1 黄色
子项遵循了本身的约束,由于他添加了定位,其余 二、3
由于没有添加定位因此和 Stack
同样大,约束被传递。
这里咱们暂时不讲定位的使用,下篇咱们系统的聊
咱们先看看效果
这里彷佛和 loose
没有啥区别啊?咱们能够看看源码后改变成如下的示例代码再看看
BgContainer(
// 添加了一个横向布局
child: Row(
children: [
// 添加了展开组件,前面讲过能够去专栏看看
Expanded(
child: Stack(
alignment: Alignment.center,
// 设置填充方式为 passthrough
fit: StackFit.passthrough,
children: [
getItem(3, width: 120, height: 120, color: Colors.purple),
getItem(2, width: 80, height: 80, color: Colors.blue),
Positioned(
left: 10,
top: 10,
child: getItem(1),
),
],
),
)
],
),
),
)
复制代码
这里咱们看到与 expand
还有些不一样,这里咱们只展开了宽度,高度仍是子项的高度。由于这里 Stack
的宽度约束是展开的屏幕宽度,直接传递给了没有添加定位的子组件,因此看到 二、3
子组件跟着变了,而 1
子组件的大小没有发生变化。
对于非定位组件会继续保持对齐方式和子项的约束
为啥不聊 overflow
呢,由于本文是基于 Flutter 2.2.1 版本的,overflow
已经被弃用了,不少地方的剪裁行为之后都统一为 clipBehavior
,这参数咱们以前在剪裁篇《Flutter 中各类剪裁 Widget 的使用》聊过,建议回看
当咱们设置的子项定位后大小超过了 Stack 布局的时候,咱们指望怎样剪裁渲染呢?是剪裁仍是显示,是带抗锯齿仍是不带呢?
BgContainer(
child: Stack(
alignment: Alignment.center,
fit: StackFit.passthrough,
// 设置剪裁行为默认 hardEdge
clipBehavior: Clip.hardEdge,
children: [
getItem(3, width: 120, height: 120, color: Colors.purple),
getItem(2, width: 80, height: 80, color: Colors.blue),
// 这里设置定位左上角 -20
Positioned(
left: -20,
top: -20,
child: getItem(1),
),
],
),
)
复制代码
Clip.none(不剪裁) | Clip.hardEdge | antiAlias、antiAliasWithSaveLayer |
---|---|---|
![]() |
![]() |
![]() |
通常是默认和不剪裁进行设置,其余的能够回看剪裁篇,介绍了不一样。
到这里 Stack 核心内容就聊完了,下篇咱们结合聊定位组件「Positioned、Align」聊聊,记得关注个人专栏哦
基于 Flutter 🔥 最新版本
👏 欢迎点赞➕收藏➕关注,有任何问题随时在下面👇评论,我会第一时间回复哦