1.2.5 Positioned
若需要精确控制若干个子组件在Stack中的位置,则开发者可选用Positioned组件,而1.2.4节也提到过,Stack在布局时会将children参数中的子组件分为二大类,一类是“有位置”的子组件,即Positioned组件,另一类是“无位置”的其他组件。由此可见Positioned和Stack两个组件紧密相连。使用时,Positioned组件一般直接作为Stack组件的子级,不再在二者之间插入其他组件,而真正需要渲染的子组件应通过Positioned的child属性嵌入,作为Positioned的子级。
1. top、bottom、left、right属性
上、下、左、右属性分别由top、bottom、left、right这4个参数设置,用于控制Positioned的子组件在Stack容器内的位置,类型为小数,单位是逻辑像素。例如,top:16.0表示上级Stack应将该Positioned的子组件沿其顶部对齐,并留白16逻辑像素,而bottom:8.0则会使Stack沿其底部对齐,并留出8单位的空白。
当top和bottom同时传入时,会迫使Positioned的child的高度约束至相应的高度。例如当Stack的尺寸为300×300单位时,同时传入top和bottom为10单位,则会迫使子组件的高度占满中间的280单位。
同样地,left和right用于设置水平方向的对齐方式和留白。单独传入时,会使子组件自动对齐在Stack的左边或右边,若同时传入则会额外增加对其子组件的约束,间接设置了它的宽度。
实战中通常不必同时使用这4个参数。例如当需要布局在Stack的左上角时,一般选择传入top和left这2个参数,而当需要沿右下角对齐时,可直接传入bottom和right这2个参数。当横轴和纵轴方向其中某个维度没有传入相关参数时,Stack会依照自身的alignment属性值处理默认的维度。
当2个维度都没有传入任何参数时,即Positioned全部位置属性(包括上述4个属性和下面要介绍的width和height属性)都为空时,Stack不会再把这个组件当作“有位置”的组件,而是直接当成Positioned不存在,对其子组件按照“无位置”方式处理。这一行为是由于Positioned本质属于Flutter框架中的ParentDataWidget类型的组件,本质目的是为父级组件提供数据。当它的属性皆为空时,也就不存在数据了。本书由于篇幅限制不再展开讨论,对此有兴趣的读者可自行探索相关知识。
2. width和height属性
除了前面介绍的top、bottom、left、right以外,开发者还可以直接通过传入宽度(width)和高度(height)参数约束子组件的尺寸。例如,当传入top:20 和height:50时,Stack会沿顶部对齐,留白20单位,并把子组件的高度约束至50单位。Stack的高度若超过70单位,即底部若还有剩余空间,则会留白;若Stack的高度低于70单位,则会发生溢出,这时Stack的clipBehavior参数可用于决定是否对溢出部分进行裁剪。
值得一提的是,同一维度的3个属性(横轴left、right、width属性,以及纵轴top、bottom、height属性)最多只可以传入2个,否则会产生运行时错误。
作为总结,这里提供一个较完整的示例,核心部分代码如下:
这个示例展示了Positioned组件中2个维度共6种不同参数的使用方式,均可用于指定子组件在Stack中的位置及尺寸。程序运行效果如图1-24所示。
另外,Stack和Positioned配合并不一定可以满足一切布局需求。例如,假设布局时需要将若干个子组件斜沿对角线排列,如图1-25所示,此种情况不太容易通过Stack实现。
图1-24 Positioned组件中的6种参数的用法展示
图1-25 Stack和Positioned不容易做出这种效果
这里的主要问题是Stack和Positioned组件无法在不确定每个子组件尺寸的前提下,正确设置top和left等属性值。当然,如果每个子组件的尺寸都是已知的,则在实战中也可以通过简单计算直接得出需要的留白距离,但若无法提前确定每个子组件的尺寸,且需要根据它们的实际尺寸动态调整布局,则可以考虑使用CustomMultiChildLayout组件,读者可参考第15章“深入布局”中的相关介绍与实例。