前言:
这是我参与8月更文挑战的第 23 天,活动详情查看:8月更文挑战。为应掘金的八月更文挑战
,我准备在本月挑选 31
个以前没有介绍过的组件,进行全面分析和属性介绍。这些文章将来会作为 Flutter 组件集录
的重要素材。希望可以坚持下去,你的支持将是我最大的动力~
1. 认识 Stack 组件
Stack
是一个经常被用到的组件,我看可以通过它来叠合若干个组件。源码中对它的介绍是:
一个将其子部件,相对于盒边缘进行定位的组件。
也就是说 Stack
组件擅长的是 定位
组件。下面是 Stack
组件类的定义
和 构造方法
,可以看出它继承自 MultiChildRenderObjectWidget
。所以可接受一个组件列表。另外还有一些配置参数,在下面将一一介绍 。
2. Stack 组件的简单使用
如下,通过 Stack
将蓝色盒子、红色盒子和一个图标叠放在一起。可以看出,默认情况下组件会以 Stack
区域的左上角进行对齐叠放,且会根据列表元素的顺序依次叠放。
Stack(
children: <Widget>[
Container( width: 100, height: 100, color: Colors.red ),
Container( width: 60, height: 60, color: Colors.green ),
Icon(Icons.ac_unit,color: Colors.white )
],
);
textDirection
属性
根据不同国家的阅读习惯,给出 textDirection
来确定排布方向。其类型为 TextDirection
枚举,有两个元素,rtl
代表从右向左,ltr
代表从左向右,我们的阅读习惯自然是 ltr
。
enum TextDirection {
/// The text flows from right to left (e.g. Arabic, Hebrew).
rtl,
/// The text flows from left to right (e.g., English, French).
ltr,
}
当把 textDirection
设为 rtl
时,效果如下,会以 Stack
区域的右上角进行对齐叠放。
Stack(
textDirection: TextDirection.rtl,
children: <Widget>[
Container( width: 100, height: 100, color: Colors.red ),
Container( width: 60, height: 60, color: Colors.green ),
Icon(Icons.ac_unit,color: Colors.white )
],
);
alignment
属性
alignment
属性我们应该非常熟悉,它的类型是 AlignmentGeometry
,用于控制对齐方式。比如将 alignment
设置为 Alignment.center
常量,就会在 Stack
中居中排布。
Stack(
alignment: Alignment.center,
children: <Widget>[
Container( width: 100, height: 100, color: Colors.red ),
Container( width: 60, height: 60, color: Colors.green ),
Icon(Icons.ac_unit,color: Colors.white )
],
);
另外 Alignment
除了八个方位外,可以自己指定对齐方式。之前再 《出神入化的Align》 一文中详细介绍了 alignment
属性的用法,这里不再赘述。比如下面是 Alignment(0.2,0.2)
的效果。
3. Stack 的 fit 属性
fit
属性类型为 StackFit
枚举,有如下三个元素。默认情况下是 loose
。
enum StackFit {
loose,
expand,
passthrough,
}
先看个例子:如下,在 Stack
外层通过 ConstrainedBox
施加一个宽高固定
的约束,毫无疑问 Stack
在被强制约束后,其占位区域被固定。
ConstrainedBox(
constraints: BoxConstraints.tightFor(width:200,height:150),
child: Stack(
fit: StackFit.loose,
children: <Widget>[
Container(width: 100, height: 100, color: Colors.red),
Container(width: 60, height: 60, color: Colors.green),
Icon(Icons.ac_unit,color: Colors.white,)
],
));
可以看出,在 StackFit.loose
的情况下,子组件并未受到该固定尺寸的约束,而是以该固定区域为上限的松散约束,如下红色所示。
此时,在 StackFit.expand
的情况下,子组件也会受到一个固定尺寸的强约束。
Container
在该约束下,会变成固定的宽高,即使蓝色自身要求是 60*60
。
其实稍微瞄一下 RenderStack
的源码我们就能知道 fit
具体的处理逻辑。在 loose
时,会通过 constraints.loosen()
将创建一个松散约束,该方法就是将最小宽高设为 0
,最大宽高设为对应最大值
。而 expand
会将自身约束的最大区域最为固定
宽高约束。最后 passthrough
则什么都不处理,直接将自身约束施加给子节点。
BoxConstraints loosen() {
assert(debugAssertIsValid());
return BoxConstraints(
minWidth: 0.0,
maxWidth: maxWidth,
minHeight: 0.0,
maxHeight: maxHeight,
);
}
4. Stack 的 overflow 与 clipBehavior 属性
我们可以清楚地看到 overflow
属性是一个过时的属性,而 clipBehavior
就是替代它的。clipBehavior
是什么我就不多说了,详见 ClipPath 一文。
那为什么需要 clipBehavior
进行裁剪呢?通过 Positioned
组件可以对子组件进行精确定位,甚至是负数。如下左边偏移 -20
,右边偏移 -10
。默认裁剪行为是 hardEdge
,效果如下:
class StackDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return
Container(
color: Colors.grey.withAlpha(33),
constraints: BoxConstraints.tightFor(width:200,height:150),
child: Stack(
children: <Widget>[
Container(width: 100, height: 100, color: Colors.red),
Positioned(
left: -20,
top: -10,
child: Container(width: 60, height: 60, color: Colors.green)),
Icon(Icons.ac_unit,color: Colors.white,)
],
));
}
}
当 clipBehavior: Clip.none,
时,不会进行裁剪,出界的就会被显示出来。注意一点,出界的部分是无法响应点击事件
的。如果需要点击,有其他组件能够解决。
那 Stack
的使用方式到这里就介绍完毕,那本文到这里就结束了,谢谢观看,明天见~