Flutter Stack 用法小结
记录一下如何使用 Flutter Stack Widget 来将文本跟图片中的某个特定点对齐。
某个产品需求要求显示本月累计签到天数,于是设计给了开发如下一张图。

设计姐姐”好心”地将文案也写好了,只留了个格让开发填,够简单吧。但其实开发很头疼:”累计签到”那一行文案是特殊字体,所以并不能吐槽设计的切图方式完全不合理。但问题是对背景图”填空”容易引起适配问题,稍有不慎就如下图一样没对齐。

接下来看如何在 Flutter 中解决上述对齐问题。涉及到的几个要点:
- 图片缩放 - 我们知道图片适配不同大小屏幕大小
- 了解
Image.fit属性 - 这个属性控制着图片如何缩放 - 了解
Stack+Positioned的基本用法 - 了解
Stack+Align的基本用法
图片缩放
我们知道图片适配不同大小屏幕时必然出现不同程度的缩放。Flutter 中使用 Image 控件显示图片,Image.fit 属性则用于控制图片缩放方式。fit 属性为枚举类型 BoxFit,最常用值的包括如下几种:
fill- Fill the target box by distorting the source’s aspect ratio.contain- As large as possible while still containing the source entirely within the target box.cover- As small as possible while still covering the entire target box.



先来看如何将背景图铺满 Stack。代码如下:
1 | Stack( |
不同参数时效果分别如下:
Image.asset(width: null, fit: null)
Image.asset(width: double.infinity, fit: null)
Image.asset(width: double.infinity, fit: BoxFit.fill)
Image.asset(width: double.infinity, fit: BoxFit.cover)
就这里的场景而言(图片高度无限制),BoxFit.cover 和 BoxFit.fill 效果无区别。不过 BoxFit.cover 保证图片不会变形,是个更好的选择。
控件定位
Stack 类用于对若干个控件以层叠方式布局。例如:
1 | Stack( |

Stack 控件的子节点要么是 positioned (Positioned 或 Align 控件) 要么是 non-positioned。Stack 控件的大小刚好包含所有的 non-positioned 子控件(这些控件默认位于 Stack 的左上角)。而 positioned 子节点的位置,则由其 top, right, bottom, left 属性来决定。
Positioned 和 Align 控件都能用于 Stack 控件定位和对齐。Positioned 以 top, right, bottom, left 属性来定位,这些属性分别用于指定控件到 Stack 各边框的距离;Align 以 Alignment(x, y) 属性来定位,这些属性分别用于指定控件水平方向和垂直方向的距离范围。
Alignment 的 x, y 属性规定如下:
Alignment(0.0, 0.0)- 表示矩形中点Alignment(-1.0, -1.0)- 表示矩形左上角Alignment(1.0, 1.0)- 表示矩形右下角
关于 Stack 有一个小细节要注意。通过如下两个例子说明:
1 | Scaffold( |
这个例子中,Text 按预期居显示:

然而,当 Stack 外面嵌套一个 Column 或 ListView 之后,Align 似乎在垂直方向失去了定位的作用。
1 | Scaffold( |

我们观察 Stack 外面嵌套一个 Column 或 ListView 之后控件树,截图如下:

所以这个现象不难理解:
- Stack 外面无嵌套时,它占满了父控件,所以
Align生效 - Stack 外面嵌套
Column或ListView时,Stack 大小未指定(最终以Text大小为准),所以Align从视觉效果上看在垂直方向不起作用。
给 Stack 指定大小就能解决这个问题。这里使用 SizedBox 指定 Stack 高度为 300。

解决方案
方案一
方案一使用 Stack + Positioned 定位,代码如下:
1 |
|
注意这里的实现细节,即按图片缩放比 scale 来调整 top 和 right,保证不同大小屏幕上都能完美适配。
方案二
方案二使用 Stack + Align 定位,代码如下:
1 | Widget build(BuildContext context) { |
注意这里的实现细节,即为 Stack 添加一个指定大小的 SizeBox(大小与图片显示的大小相同),保证不同大小屏幕上都能完美适配。

如果不为 Stack 指定大小,则 Align 在垂直方向不生效。如下图:
