Flutter 布局之 Row & Column & Stack
1 Row Column Stack 简介
Flutter 根据 x y z 三个坐标轴方向分别对应 Row Column Stack 三个Widget, 它们都有一个 Widget 类型的数组 children.
-
Row的children中的子组件是按照水平方向(即主轴方向)排列的, 还有一个交叉轴是垂直于主轴方向(即垂直方向), 通过主轴和交叉轴方向上的对齐方式来控制子组件的分布方式. -
Column的children中的子组件是按照垂直方向(即主轴方向)排列的, 还有一个交叉轴是垂直于主轴方向(即水平方向), 通过主轴和交叉轴方向上的对齐方式来控制子组件的分布方式. -
Stack的children中的子组件是按照z轴方向叠起来的, 第一个在最底层, 依次叠加, 飘浮于上方.
接下来的示例代码主体部分不变, 变化的主要是 Widget layoutStyle() 这个函数, layoutStyle() 的实现在各部分的示例中给出, 各部分的效果给出截图, 截图的黑色边框就是模拟器的边框了, 整个模拟器图片比较大, 因此如果不是必须, 就只截一部分.
- 代码主体部分
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Scaffold(
appBar: AppBar(title: const Text('Layout Demo'),),
body: Center(
child: layoutStyle(),
),
),
);
}
}
2 Row
Row 的 children 中的子组件是按照数组中的顺序, 从start开始沿水平方向排列, 所有组件宽度总和不能超过 Row 最大能延伸的宽度, 否则会因为放不下而报错, 并不会像 iOS 原生超出的部分不显示. Row 的高度默认以子组件中最大的高度为准.
注意:
2.12.22.3都是在textDirection和verticalDirection默认值时的效果, 具体原因在2.42.5说明, 这两个一般我们也不会去修改.
Widget layoutStyle()函数 我们本例的代码外层Container高度设置为 100, 与最大的子组件高度相同, 具体用意会在crossAxisAlignment中体现出来.
Widget layoutStyle() {
return Container(
color: Colors.yellow,
height: 100,
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisSize: MainAxisSize.max,
textDirection: TextDirection.ltr,
verticalDirection: VerticalDirection.down,
children: [
Container(
color: Colors.grey,
alignment: Alignment.center,
width: 60,
height: 80,
child: const Text(
'C 1',
style: TextStyle(fontSize: 20,),
),
),
Container(
color: Colors.green,
alignment: Alignment.center,
width: 100,
height: 100,
child: const Text(
'C 2',
style: TextStyle(fontSize: 20,),
),
),
Container(
color: Colors.blue,
alignment: Alignment.center,
width: 80,
height: 80,
child: const Text(
'C 3',
style: TextStyle(fontSize: 20,),
),
),
],
),
);
}
2.1 mainAxisAlignment 主轴对齐
mainAxisAlignment 排列方式默认是 start. 后边给出每种对齐方式一个截图和必要的说明, 只是修改了核心代码部分, 如下
mainAxisAlignment: MainAxisAlignment.start
2.1.1 start
第一个子组件的左边与 Row 的左边对齐, 空出右边的距离, 也是默认方式.
mainAxisAlignment: MainAxisAlignment.start

2.1.2 center
所有子组件排列到一起, 居中分布, 即第一个子组件左边到 Row 的左边的边距 与 最后一个子组件的右边到 Row 的右边的边距是相同的(m1 == m2).
mainAxisAlignment: MainAxisAlignment.center

2.1.3 end
最后一个子组件的右边与 Row 的右边对齐, 空出左边的距离.
mainAxisAlignment: MainAxisAlignment.end

2.1.4 spaceBetween
第一个子组件的左边与 Row 的左边对齐, 最后一个子组件的右边与 Row 的右边对齐, 剩余的距离平均分给组件之间的间距. 如果剩余总宽度是 w, 子组件个数为 n, 则间距 s 计算公式为 s = w / (n - 1).
mainAxisAlignment: MainAxisAlignment.spaceBetween

2.1.5 spaceAround
假设有 n 个子组件, 剩余总宽度是 w, 把剩余的平均分成 n, 每个子组件分到 w/n, 然后分布再自身左右两边, 即 w/2n, 类似于外边距, 此示例中的间距最终比例为 1:2:2:1, 组件之间的间距是 w/n, 第一个和最后一个边距是 w/2n
mainAxisAlignment: MainAxisAlignment.spaceAround

2.1.6 spaceEvenly
组件的间距和组件的边距是相等的. 假设有 n 个子组件, 剩余总宽度是 w, 每个距离是 w/(n + 1)
mainAxisAlignment: MainAxisAlignment.spaceEvenly

2.2 crossAxisAlignment 交叉轴对齐
crossAxisAlignment 默认是 center, 后边给出每种对齐方式一个截图和必要的说明, 只是修改了核心代码部分, 如下
crossAxisAlignment: CrossAxisAlignment.center,
2.2.1 start
所有子组件的顶部与 Row 的顶部对齐.
crossAxisAlignment: CrossAxisAlignment.start,

2.2.2 center
所有子组件垂直居中, 中心点在同一第水平线上.
crossAxisAlignment: CrossAxisAlignment.center,

2.2.3 end
所有子组件的底部与 Row 的底部对齐.
crossAxisAlignment: CrossAxisAlignment.end,

2.2.4 stretch
将所有子组件的高度拉伸到与 Row 相同的高度, 此时 Row 的高度已经不是以高度最大那个组件为准了, 只要 Row 在交叉轴方向上有扩大的空间, 就会被最大限度的撑开. 此示例设置外层 Container 的高度, 就是因为不设置的话会填满屏幕剩余的高度, 所以也给出了将高度改为 200 的效果,
crossAxisAlignment: CrossAxisAlignment.stretch,

- 将高度改为 200 的效果

2.2.5 baseline
baseline 简单理解就是文字的底部对齐. 但是当 crossAxisAlignment 被设置为 baseline 时, 必须设置 Row 的 textBaseline 属性, 这是一个枚举值, 有两个 case, 分别是 alphabetic -> 英文, ideographic -> 中文, 中英混合的时候效果就不是很好了.
此示例需要我们先把 C 2 的字号改为 40, C 3 的字号改为 80, 才能明显看出效果.
crossAxisAlignment: CrossAxisAlignment.baseline,
// alphabetic 英文
// ideographic 中文
textBaseline: TextBaseline.alphabetic,

2.3 mainAxisSize 主轴尺寸
mainAxisSize 就是主轴方向上的尺寸, 其实就是宽度,
2.3.1 max
默认是 max, 设置为 max, 向右最大限度扩张, 此示例就会到屏幕右边, 就是整个屏幕的宽度.
mainAxisSize: MainAxisSize.max

2.3.2 min
设置为 min, 就是宽度刚刚好是所有组件宽度的总和. 因为外层的 Container 是居中显示的, 所以最终 Row 就在屏幕中间显示.
mainAxisSize: MainAxisSize.min

2.4 textDirection 水平排列方向
textDirection 控制子组件水平方向的起始位置, 是一个枚举属性, 默认为 ltr, TextDirection 枚举有 ltr 和 rtl 两个值.
ltr: 表示水平方向的start在左边,end在右边,rtl: 表示水平方向的start在右边,end在左边.
2.4.1 ltr
默认设置.
mainAxisAlignment: MainAxisAlignment.start,
textDirection: TextDirection.ltr,

2.4.2 rtl
同时把 mainAxisAlignment 设置为 end.
mainAxisAlignment: MainAxisAlignment.end,
textDirection: TextDirection.rtl,

2.5 verticalDirection 垂直排列方向
verticalDirection 控制子组件垂直方向的起始位置, 是一个枚举属性, 默认为 down, VerticalDirection 有 up 和 down 两个值. 在 Row 中它的设置会影响到 crossAxisAlignment.
down: 表示垂直方向的start在顶部,end在底部.up: 表示垂直方向的start在底部,end在顶部.
2.5.1 down
默认设置, start 在上边, end 在下边..
crossAxisAlignment: CrossAxisAlignment.end,
verticalDirection: VerticalDirection.down,

2.5.2 up
start 在下边, end 在上边.
crossAxisAlignment: CrossAxisAlignment.end,
verticalDirection: VerticalDirection.up,

3 Column
Column 的 children 中的子组件是按照数组中的顺序, 从start沿垂直方向排列, 所有组件高度总和不能超过 Column 最大能延伸的高度, 否则会因为放不下而报错, 并不会像 iOS 原生超出的部分不显示. Column 的宽度默认以子组件中最大的宽度为准.
注意:
Column是不能滚动, 所以最大高度也不能超过屏幕.2.12.22.3都是在textDirection和verticalDirection默认值时的效果, 具体原因在2.42.5说明, 这两个一般我们也不会去修改.
我们本例的代码外层 Container 宽度设置为 150, 与最大的子组件宽度相同, 具体用意会在 crossAxisAlignment 中体现出来.
Column 与 Row 只是排列方向不同, 原理是一样的. 所以就不做过多阐述了, 直接上图看效果.
Widget layoutStyle()函数
Widget layoutStyle() {
return Container(
width: 150,
color: Colors.yellow,
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisSize: MainAxisSize.max,
textDirection: TextDirection.ltr,
verticalDirection: VerticalDirection.down,
children: [
Container(
color: Colors.grey,
alignment: Alignment.center,
width: 80,
height: 80,
child: const Text(
'C 1',
style: TextStyle(fontSize: 20,),
),
),
Container(
color: Colors.green,
alignment: Alignment.center,
width: 150,
height: 150,
child: const Text(
'C 2',
style: TextStyle(fontSize: 20,),
),
),
Container(
color: Colors.blue,
alignment: Alignment.center,
width: 100,
height: 120,
child: const Text(
'C 3',
style: TextStyle(fontSize: 20,),
),
),
],
),
);
}
3.1 mainAxisAlignment 主轴对齐
改变的核心代码是主轴对齐方式, 如下
mainAxisAlignment: MainAxisAlignment.start,
3.1.1 start
mainAxisAlignment: MainAxisAlignment.start,

3.1.2 center
mainAxisAlignment: MainAxisAlignment.center,

3.1.3 end
mainAxisAlignment: MainAxisAlignment.end,

3.1.4 spaceBetween
mainAxisAlignment: MainAxisAlignment.spaceBetween,

3.1.5 spaceAround
mainAxisAlignment: MainAxisAlignment.spaceAround,

3.1.6 spaceEvenly
mainAxisAlignment: MainAxisAlignment.spaceEvenly,

3.2 crossAxisAlignment 交叉轴对齐
改变的核心代码是主轴对齐方式, 如下
crossAxisAlignment: CrossAxisAlignment.center,
3.2.1 start
crossAxisAlignment: CrossAxisAlignment.start,

3.2.2 center
crossAxisAlignment: CrossAxisAlignment.center,

3.2.3 end
crossAxisAlignment: CrossAxisAlignment.end,

3.2.4 stretch
crossAxisAlignment: CrossAxisAlignment.stretch,

3.2.4 baseline
baseline 设置之后, 与 start 效果相同, 我个人理解好像没什么用, 在 Column 还没有找到使用场景, 暂时先不做说明, 如果哪位小伙伴知道使用场景, 请不吝赐教, 表示感谢.
3.3 mainAxisSize 主轴尺寸
改变的核心代码是主轴对齐方式, 如下
mainAxisSize: MainAxisSize.max,
3.3.1 max
mainAxisSize: MainAxisSize.max,

3.3.2 min
mainAxisSize: MainAxisSize.min,

3.4 textDirection 水平排列方向
crossAxisAlignment: CrossAxisAlignment.end,
textDirection: TextDirection.rtl,

3.5 verticalDirection 垂直排列方向
mainAxisAlignment: MainAxisAlignment.end,
verticalDirection: VerticalDirection.up,

4 Stack
Stack 的 children 中的子组件是按照z轴方向叠起来的, 第一个在最底层, 依次叠加, 飘浮于上方.
textDirection 控制水平方向 start 和 end 的位置, 在 Row 部分已经说明, 此处就不再说了.
宽按照最宽的子组件为准, 高按照最高的子组件为准.
Widget layoutStyle() {
return Container(
color: Colors.yellow,
padding: const EdgeInsets.all(20),
child: Stack(
alignment: AlignmentDirectional.topStart,
textDirection: TextDirection.ltr,
fit: StackFit.loose,
clipBehavior: Clip.hardEdge,
children: [
Container(
color: Colors.green,
alignment: Alignment.center,
width: 150,
height: 100,
child: const Text(
'C 1',
style: TextStyle(fontSize: 20,),
),
),
Container(
color: Colors.grey,
alignment: Alignment.center,
width: 100,
height: 150,
child: const Text(
'C 2',
style: TextStyle(fontSize: 20,),
),
),
Container(
color: Colors.blue,
alignment: Alignment.center,
width: 50,
height: 50,
child: const Text(
'C 3',
style: TextStyle(fontSize: 20,),
),
),
],
),
);
}
4.1 alignment 对齐方式
对齐方式系统给出 9 种常用方式, 同时也可以根据自己的需求, 来设置 x 轴y轴的比例. textDirection 默认为 ltr , 水平方向 start 在左边, end 在右边, 当设置 rtl 时start 和 end 的位置互换.
修改的核心代码如下:
alignment: AlignmentDirectional.topStart,
4.1.1 topStart
所有子组件左上角对齐.
alignment: AlignmentDirectional.topStart,

4.1.2 topCenter
所有子组件顶部水平中心点对齐.
alignment: AlignmentDirectional.topCenter,

4.1.3 topEnd
所有子组件右上角对齐.
alignment: AlignmentDirectional.topEnd,

4.1.4 centerStart
所有子组件左边垂直中心点对齐.
alignment: AlignmentDirectional.centerStart,

4.1.5 center
所有子组件中心点对齐.
alignment: AlignmentDirectional.center,

4.1.6 centerEnd
所有子组件右边垂直中心点对齐.
alignment: AlignmentDirectional.centerEnd,

4.1.7 bottomStart
所有子组件左下角对齐.
alignment: AlignmentDirectional.bottomStart,

4.1.8 bottomCenter
所有子组件底部中心对齐.
alignment: AlignmentDirectional.bottomCenter,

4.1.9 bottomEnd
alignment: AlignmentDirectional.bottomEnd,

4.1.10 自定义
alignment: const AlignmentDirectional(-0.5, -0.5),

4.2 textDirection
ltr 默认设置上边已经全部列出了, 就不再重复了, 下面主要看一下 rtl 的情况, 主要就是 start 与 end 位置互换, 只展示期中一种情况, 其他依次类推, 大家自己体验就好.
4.2.1 rtl
所有子组件右上角对齐.
alignment: AlignmentDirectional.topStart,
textDirection: TextDirection.rtl,

Stack 其他几个属性的用法和场景还没有研究, 后续找时间再探索吧...
总结
Row Column Stack 主要用于多个子组件的布局, 是 Flutter 中常用的布局组件, 像下图这种就很适合, 这个是自己随便画的, 不要介意😄.

转载自:https://juejin.cn/post/7017757399033315365