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.1
2.2
2.3
都是在textDirection
和verticalDirection
默认值时的效果, 具体原因在2.4
2.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.1
2.2
2.3
都是在textDirection
和verticalDirection
默认值时的效果, 具体原因在2.4
2.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