大家好,我是 17。
新手礼包一共 3 篇文章,每篇都是描述尽量详细,实例讲解,包会!
本篇介绍 Row 的用法,用实例讲解 flex 弹性布局原理。本来在 Flutter 弹性布局的基石: Flex 和 Flexible 一文中的内容已经包含 Row 了,但因为 Row 太常用了,所以单写一篇。
默认情况下, Row 在宽度上尽量大,在高度上只要能包住所有的 children 即可。
第一个示例给出全部代码,后面的只给出 Row 的代码。在 Row 的外面加一个 Container ,是为了给 Row 加一个 border,方便查看。减去 margin,padding,border 后 ,Row 的宽度为 300。
MaterialApp(
home: Scaffold(
body: Container(
width: 342,
alignment: Alignment.center,
child: Container(
padding: const EdgeInsets.all(10),
margin: const EdgeInsets.all(10),
decoration:
BoxDecoration(border: Border.all(color: Colors.blue)),
child: Row(
children: [
Container(
width: 100,
height: 50,
color: const Color.fromARGB(255, 82, 143, 222),
),
],
)))));
在本例中,Row 的宽度达到允许的最大值 300。高度为 50,正好可以包含蓝色块的高度。MainAxisSize.min
可以让 Row 的宽度也正好能包含 children。
Row(
mainAxisSize: MainAxisSize.min,
children: [
Container(
width: 100,
height: 50,
color: const Color.fromARGB(255, 82, 143, 222),
),
],
);
mainAxisSize:MainAxisSize.min
让 Row 的宽度收缩,直到正好包含 所有 children 为止。本例中,正好包含蓝色块,最终宽度为 100。
mainAxisSize 的默认值是
mainAxisSize.max
非弹性布局是说在 children 中没有 Expanded,Flexible 这种有 flex 参数的 child。
Row(
children: [
Container(
width: 100,
height: 50,
color: const Color.fromARGB(255, 82, 143, 222),
),
const Text(
"IAM17",
style:
TextStyle(color: Color(0xFFC45F84), fontSize: 24),
)
],
);
非弹性布局中,Row 的 children 在宽度方面没有限制, child 按自己期望的尺寸在水平方向依次排列。如果 children 的总宽度没有超过 Row 的宽度,没有什么问题。如果超过了 Row 的宽度,在开发环境下,会给出警告。
比如修改 Container 的 width:100 为 width:400
,这个时候 Row 已经没有多余的空间给 Text 了,甚是连 Container 也放不下。
在生产环境中,多出来的部分会被直接截断。
弹性布局是说在 children 中有 Expanded,Flexible 这种有 flex 参数的 child。
简单来说,Row 分配空间的过程是这样的。
这里说的 Flexible 的 fit 参数的值为 FlexFit.loose
。Expanded 就是 fit 参数为 FlexFit.tight
的 Flexible。
Row(
children: [
Expanded(
child: Container(
width: 100,
height: 50,
color: const Color.fromARGB(255, 82, 143, 222),
)),
const Text(
"IAM17",
style:
TextStyle(color: Color(0xFFC45F84), fontSize: 24),
)
],
)
Row 的宽度为 300,先给非弹性块 Text 分配固定大小空间,剩余的全分给的 Expanded。child 蓝色块占用所有 Expanded 分配到的空间。
我们注意到 Container 的 width 是 100,实际上,就算是这里写 0,或写 1000 都没有关系,用 Expanded 包起来的 child 的 width 属性会被忽略。
Expanded 包起来的 child 的 width 是不能自定义的,如果 child 要自定义 width 又要保持弹性布局怎么办?用 Flexible!
在下面的例子中 Row 总的可用宽度为 300,两个 Container 各占 100,还余 100 空白在两个 Container 之间
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Container(
width: 100,
height: 50,
color: Colors.blue,
),
Container(
width: 100,
height: 50,
color: Colors.red,
),
],
)
现在我们把第一个 container 用 Flexible 包起来。
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Flexible(
child: Container(
width: 100,
height: 50,
color: Colors.blue,
)),
Container(
width: 100,
height: 50,
color: Colors.red,
),
],
)
重新执行查看效果,发现没什么变化。这是因为 总宽度为 300,分给非弹性红色块 100后还有 200, 唯一的弹性块拿到 200。蓝色块的 100 在分配到的空间范围内,所以没有什么反应。
把第一个 Container 的 width 加大到 150 查看效果,发现第一个 Container 的宽度变为 150 了。同理 150 也在 分配到的 200 之内。
继续加大 width 的宽度到 200,发现他们已经紧贴到一起了。继续加大就没有任何效果了,但也不会报错。
继续加大到 200 以上就超过分配到的 200了,所以宽度不再增加。不会报错是因为蓝色块被 Flexible 限制在 200 以内,加上红色块的总宽度在 300 以内,没有超出,当然不会报错。
如果左面 Container 的 宽度不是我们指定的,而是 Container 的 child 撑起来的,那么就可以实现宽度自适应的布局效果,不用担心会超出边界。
Flexible 的 flex 决定了可以分配多少剩余空间。fit 参数决定 child 能否自行决定大小。
看下面的的例子,红色块为固定宽度,绿蓝为弹性宽度。在fit: FlexFit.tight
的情况下,绿色块和红色块的 width 无效。因为 Flexible 的 flex 已经决定了宽度值,child 只能用这个值不能修改。
Row(children: [
Container(
width: 20, height: 50, color: Colors.red),
Flexible(
fit: FlexFit.tight,
flex: 1,
child: Container(
width: 100, height: 50, color: Colors.green)),
Flexible(
fit: FlexFit.tight,
flex: 2,
child: Container(
width: 100, height: 50, color: Colors.blue))
]
本例中 Row 的宽度为 320,首先分配 20 给固定宽度的红色块,剩余的 300 由两个弹性块瓜分。根据 flex 值,绿色块得到 100,蓝色块得到 200。Flex 值越大,得到的空间越大。
fit: FlexFit.tight
的 Flexible ,一般是用 Expanded。
在fit: FlexFit.loose
的情况下,绿色块和红色块的 width 是有作用的,可以在 0 和最大值之间自定义自己的宽度。
我们把第二个 Flexible 的 fit: FlexFit.tight 修改为 fit: FlexFit.loose
,蓝色块的 width 起作用了,显示为 100 宽。
我们调整一下摆放方式,蓝色块省出来的 100 空间被填充到各个块之间了。
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
...
我们发现 Container 和 文本紧挨在一起了,想要他们之间有一个距离。可以用 Padding 把 Container 或 Text 包起来,但是这样写起来比较麻烦,而且多了一个层级,也不美观,不如用 SizedBox。
Row(
children: [
Container(
width: 100,
height: 50,
color: const Color.fromARGB(255, 82, 143, 222),
),
const SizedBox(width: 20,),
const Text(
"IAM17",
style:
TextStyle(color: Color(0xFFC45F84), fontSize: 24),
)
],
)
Row(
children: [
Container(
width: 100,
height: 50,
color: const Color.fromARGB(255, 82, 143, 222),
),
const Spacer(),
const Text(
"IAM17",
style:
TextStyle(color: Color(0xFFC45F84), fontSize: 24),
)
],
)
文本 “IAM17” 与 蓝色块 之间的空白是 文本 “IAM17” 与 文本 “Flutter” 之间空白宽度的两倍。Row 的宽度如果增加或缩小,空白的宽度也会增加会缩小,但会保持两倍的关系。
平均分配空白用 mainAxisAlignment 参数,具体用法详见 Flutter Wrap 图例。虽然讲的是 Wrap Widget,但是 alignment 与 Row 的 mainAxisAlignment 用法是一样的。比如两端对齐:
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
...
Row 嵌套的时候需要注意下,因为一不小心会报错。
Row(
children: [
Row(children: [
Expanded(
child: Container(
width: 100,
height: 50,
color: Color.fromARGB(255, 210, 74, 137),
))
]),
],
)
Expanded 是要占据所有的可用空间,内层的 Row 在宽度可以是无限,Expanded 无法占据无限空间,所以报错。解决的办法很简单,让 内层的 Row 的宽度有限就可以了。
Row(
children: [
Expanded(
child: Row(children: [
Expanded(
child: Container(
width: 100,
height: 50,
color: Color.fromARGB(255, 210, 74, 137),
))
])),
],
)
或者给 Row 加一个宽度约束。比如用 SizedBox 包起来。
Row(
children: [
SizedBox(
width: 150,
child: Row(children: [
Expanded(
child: Container(
width: 100,
height: 50,
color: Color.fromARGB(255, 210, 74, 137),
))
])),
],
);
用 Flexible 包起来也是可以的,Flexible 与 Expanded 的区别在于 Flexible 给 child loose 约束,Expanded 给 child tight 约束。通俗一点的说法是 Flexible 的 child 的宽度可以从 0 到 最大值之间自己决定。Expanded 的 child 的宽度只能是固定值,不能修改。
Row 嵌套的时候报错本质上是因为宽度无限,遇到其它宽度无限的场景也会出现这样的问题,比如 ListView 横向滚动的时候,把 Row 嵌套在 ListView 中也会有类似的问题。
ListView(
scrollDirection: Axis.horizontal,
children: [
Row(children: [
Expanded(
child: Container(
width: 100,
height: 50,
color: Color.fromARGB(255, 210, 74, 137),
))
]),
],
)
解决办法相同,也是把 Row 用 Flexible 或 Expanded 包起来,或加一个宽度约束。
ListView(
scrollDirection: Axis.horizontal,
children: [
Expanded(
child: Row(children: [
Expanded(
child: Container(
width: 100,
height: 50,
color: Color.fromARGB(255, 210, 74, 137),
))
])),
],
);
到这里 Flutter Row 的常用的用法就都介绍完了。谢谢观看!