所谓线性布局,即指沿水平或垂直方向排布子组件。Flutter中通过Row
和Column
来实现线性布局,类似于Android中的LinearLayout
控件。Row
和Column
都继承自Flex
,我们将在弹性布局一节中详细介绍Flex
。
主轴和纵轴
对于线性布局,有主轴和纵轴之分,如果布局是沿水平方向,那么主轴就是指水平方向,而纵轴即垂直方向;如果布局沿垂直方向,那么主轴就是指垂直方向,而纵轴就是水平方向。在线性布局中,有两个定义对齐方式的枚举类MainAxisAlignment
和CrossAxisAlignment
,分别代表主轴对齐和纵轴对齐。
Row
https://api.flutter.dev/flutter/widgets/Row-class.html
Row可以在水平方向排列其子widget。定义如下:
Row({
Key key,
MainAxisAlignment mainAxisAlignment = MainAxisAlignment.start, //主轴的排序方式
MainAxisSize mainAxisSize = MainAxisSize.max, //主轴方向占用的空间
CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.center, //次轴的排序方式
TextDirection textDirection, //主轴方向子组件排列顺序
VerticalDirection verticalDirection = VerticalDirection.down, //次轴方向子组件的排列顺序
TextBaseline textBaseline = TextBaseline.alphabetic,
List<Widget> children = const <Widget>[],
})
textDirection
:主轴的布局方向,默认为系统当前Locale环境的文本方向(如中文、英语都是从左往右,而阿拉伯语是从右往左)。- ltr:从左到右。
- rtl:从右到左。
mainAxisAlignment
:主轴的对齐方式,默认为MainAxisAlignment.start
。注意:如果mainAxisSize
值为MainAxisSize.min
,则此属性无意义,因为子组件的宽度等于Row
的宽度。只有当mainAxisSize
的值为MainAxisSize.max
时,此属性才有意义。mainAxisAlignmen
t 的参考系是textDirection
。- start:左对齐。如
textDirection
取值为TextDirection.ltr
时,则MainAxisAlignment.start
表示左对齐,textDirection
取值为TextDirection.rtl
时表示从右对齐。 - center:居中对齐。
- end:右对齐。
- spaceBetween:两端对齐。项目之间的间隔相等,项目与边框的间隔为0。
- spaceAround:项目之间的间隔 相等,且是 项目与边框的间隔 的2倍。
- spaceEvenly:项目之间的间隔、项目与边框的间隔 全部相等。
- start:左对齐。如
verticalDirection
:纵轴的布局方向,默认是VerticalDirection.down
。- down: 从上往下。
- up :从下往上。(从底部开始,并垂直堆叠到顶部,对齐方式的 start 在底部,end 在顶部)
crossAxisAlignment
:纵轴的对齐方式,默认为:CrossAxisAlignment.center
。Row的高度等于子组件中最高的子元素高度,crossAxisAlignment
的参考系是verticalDirection
。- start:顶部对齐。
verticalDirection
值为VerticalDirection.down
时crossAxisAlignment.start
指顶部对齐,verticalDirection
值为VerticalDirection.up
时,crossAxisAlignment.start
指底部对齐。 - center:居中对齐。
- end:底部对齐。
- stretch:拉伸。
- baseline:基线对齐。
- start:顶部对齐。
mainAxisSize
:在主轴方向占用的空间,默认是MainAxisSize.max
。- max:表示尽可能多的占用水平方向的空间,此时无论子widgets实际占用多少水平空间,
Row
的宽度始终等于水平方向的最大宽度; - min:表示尽可能少的占用水平空间,当子组件没有占满水平剩余空间,则
Row
的实际宽度等于所有子组件占用的的水平空间;
- max:表示尽可能多的占用水平方向的空间,此时无论子widgets实际占用多少水平空间,
children
:子组件数组。
主轴对齐方式:
spaceAround 和 spaceEvenly 区别是:
- spaceAround :第一个子控件距开始位置和最后一个子控件距结尾位置是其他子控件间距的一半。
- spaceEvenly : 所有间距一样。
次轴对齐方式:
示例:
class IconContainer extends StatelessWidget {
double width;
double height;
double size;
IconData icon;
Color color;
IconContainer({
key,
this.width = 50.0,
this.height = 50.0,
this.size = 32.0,
@required this.icon,
this.color = Colors.white,
}) : super(key: key);
@override
build(context) {
return Container(
width: width,
height: width,
color: Color(Base.getRandomColor()),
child: Center(
child: Icon(icon, size: size, color: color),
),
);
}
}
mainAxisSize
Container(
height: 100,
color: Colors.grey,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min, //如果为min, mainAxisAlignment也就没意义上了
children: [
IconContainer(icon: Icons.home, width: 40.0),
IconContainer(icon: Icons.search),
IconContainer(icon: Icons.arrow_back, width: 60.0),
],
),
);
textDirection
Row(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
textDirection: TextDirection.rtl,
children: [
IconContainer(icon: Icons.home, width: 40.0),
IconContainer(icon: Icons.search),
IconContainer(icon: Icons.arrow_back, width: 60.0),
],
),
verticalDirection
Row(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
verticalDirection: VerticalDirection.up,
children: [
IconContainer(icon: Icons.home, width: 40.0),
IconContainer(icon: Icons.search),
IconContainer(icon: Icons.arrow_back, width: 60.0),
],
),
Column
Column
可以在垂直方向排列其子组件。参数和Row
一样,不同的是布局方向为垂直,主轴纵轴正好相反,可类比Row
来理解。
实际上,Row
和Column
都只会在主轴方向占用尽可能大的空间,而纵轴的长度则取决于他们最大子元素的长度。如果我们想让里面控件在整个手机屏幕中间对齐,我们有两种方法:
将
Column
的宽度指定为屏幕宽度;这很简单,我们可以通过ConstrainedBox
或SizedBox
来强制更改宽度限制,例如:Container(
constraints: BoxConstraints(minWidth: double.infinity),
color: Colors.grey,
child: Column(
// crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
IconContainer(icon: Icons.home, width: 40.0),
IconContainer(icon: Icons.search),
IconContainer(icon: Icons.arrow_back, width: 60.0),
],
),
);
将
minWidth
设为double.infinity
,可以使宽度占用尽可能多的空间。- 使用
Center
Widget;
特殊情况
如果Row
里面嵌套Row
,或者Column
里面再嵌套Column
,那么只有最外面的Row
或Column
会占用尽可能大的空间,里面Row
或Column
所占用的空间为实际大小,下面以Column
为例说明:
Container(
padding: EdgeInsets.all(16),
color: Colors.grey,
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
mainAxisSize: MainAxisSize.max, //有效,外层Colum高度为整个屏幕
children: [
Container(
color: Colors.green,
child: Column(
mainAxisSize: MainAxisSize.max, //无效,内层Colum高度为实际高度
children: [
IconContainer(icon: Icons.home, width: 40.0),
IconContainer(icon: Icons.search),
IconContainer(icon: Icons.arrow_back, width: 60.0),
Text('hello'),
],
),
),
],
),
);
如果想让里面的Column占满外部的Column,可以使用 Expanded 组件:
Container(
padding: EdgeInsets.all(16),
color: Colors.grey,
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
mainAxisSize: MainAxisSize.max, //有效,外层Colum高度为整个屏幕
children: [
Expanded(
child: Container(
color: Colors.green,
child: Column(
mainAxisSize: MainAxisSize.max, //无效,内层Colum高度为实际高度
children: [
IconContainer(icon: Icons.home, width: 40.0),
IconContainer(icon: Icons.search),
IconContainer(icon: Icons.arrow_back, width: 60.0),
Text('hello'),
],
),
),
),
],
),
);