在开发中,我们经常需要将多个Widget放在一起进行布局,比如水平方向、垂直方向排列,甚至有时候需要他们进行层叠,比如图片上面放一段文字等;
这个时候我们需要使用多子布局组件(Multi-child layout widgets)。
比较常用的多子布局组件是Row、Column、Stack。
Row组件和Column组件都继承自Flex;
- Flex组件和Row、Column属性主要的区别就是多一个 direction;
- 当direction的值为Axis.horizontal的时候,则是Row,呈一行排布;
- 当direction的值为Axis.vertical的时候,则是Column,呈一列排布;
它们都有主轴(Main Axis)和交叉轴(Cross Axis)的概念:
-
对于Row来说,水平方向是主轴,竖直方向是交叉轴;
-
对于Column来说,竖直方向是主轴,水平方向是交叉轴;
2. Row组件
Row({
Key key,
MainAxisAlignment mainAxisAlignment = MainAxisAlignment.start, // 主轴对齐方式
MainAxisSize mainAxisSize = MainAxisSize.max, // 水平方向尽可能大
CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.center, // 交叉处对齐方式
TextDirection textDirection, // 水平方向子widget的布局顺序(默认为系统当前Locale环境的文本方向(如中文、英语都是从左往右,而阿拉伯语是从右往左))
VerticalDirection verticalDirection = VerticalDirection.down, // 表示Row纵轴(垂直)的对齐方向
Textbaseline textbaseline, // 如果上面是baseline对齐方式,那么选择什么模式(有两种可选)
List children = const [],
})
2.1. mainAxisSize
Row的特点:
-
水平方向尽可能占据较大的空间;
-
垂直方向包裹内容;
-
如果水平方向也希望包裹内容,那么设置mainAxisSize = min;
return RaisedButton(
onPressed: (){
},
color: Colors.red,
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.favorite),
Text("收藏"),
],
),
);
2.2. mainAxisAlignment
元素在Row主轴上的布局方式,它是一个枚举类型:
- start :主轴开始的位置挨个摆放元素;
- end :主轴结束的位置挨个摆放元素;
- center :主轴的中心点对齐;
- spaceBetween: 左右两边的间距为0,其他元素之间平分间距;
- spaceAround:左右两边的间距是其他元素的间距的一半;
- spaceEvenly:间距平分;
class _YZHomeContentState extends State2.3. CrossAxisAlignment{ @override Widget build(BuildContext context) { return Row( children: [ Container(width: 80, height: 60, color: Colors.red,), Container(width: 120, height: 100, color: Colors.orange,), Container(width: 90, height: 80, color: Colors.blue,), Container(width: 50, height: 50, color: Colors.green,), ], mainAxisAlignment: MainAxisAlignment.spaceEvenly, ); } }
crossAxisAlignment: CrossAxisAlignment.end,
元素在Row交叉轴上的布局方式
- start:交叉轴的起始位置对齐;
- end:交叉轴的结束位置对齐(垂直方向是包裹内容的);
- center:中心点对齐(默认值)
- baseLine:基线对齐;(必须有文本才有效果)
- stretch: 先将Row占据交叉轴尽可能大的空间,再将所有的子Widget拉伸到最大;
2.4. Expanded
空间分配:拉伸或收缩;
- 如果控件之间有间隔:拉伸;
- 如果控件组合宽度超过了屏幕,则压缩;
如上图,间隔等分,如果想把所有间隔分配给第一个;
return Row(
children: [
Expanded(
child: Container(width: 80, height: 60, color: Colors.red,),
),
Container(width: 120, height: 100, color: Colors.orange,),
Container(width: 90, height: 80, color: Colors.blue,),
Container(width: 50, height: 50, color: Colors.green,),
],
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.end,
);
}
}
效果:
- 第一个控件宽度做了拉伸;
Expanded(
flex: 1,
child: Container(width: 80, height: 60, color: Colors.red,),
),
Expanded(
flex: 2,
child: Container(width: 40, height: 60, color: Colors.green,),
),
剩余空间分配比例: 当有多个Expanded时,
- 如果flex相等,则拉伸的宽度相同;
- 如果flex不等,按比例拉伸,如上面flex:2控件的宽度拉伸为flex: 1宽度的2倍,原来的width数值不再起作用。
3. Column组件
同Row;
4. Stack组件
在开发中,我们多个组件很有可能需要重叠显示,比如在一张图片上显示文字或者一个按钮等。在Flutter中我们需要使用层叠布局Stack。
4.1 Stack介绍Stack的大小默认是包裹内容的.
- alignment:从什么位置开始排布所有的子Widget;
- fit: expand 将子元素拉伸到尽可能大;
- overflow:超出部分如何处理;
Stack({
Key key,
this.alignment = AlignmentDirectional.topStart,
this.textDirection,
this.fit = StackFit.loose,
this.overflow = Overflow.clip,
this.clipBehavior = Clip.hardEdge,
List children = const [],
})
4.2 示例
class _YZHomeContentState extends State{ @override Widget build(BuildContext context) { return Stack( alignment: AlignmentDirectional.bottomCenter, overflow: Overflow.visible, children: [ Image.asset("assets/images/image_01.png",), Container(width: 150, height: 50, color: Colors.red,), // Positioned( //文字在Stack内部相对布局调整 // right: 10, // bottom: 20, // child: Text("这是图片上的文字"), // ), ], ); } }
class _YZHomeContentState extends State{ bool _isFavor = false; @override Widget build(BuildContext context) { return Stack( children: [ Image.asset("assets/images/image_02.jpeg",width: MediaQuery.of(context).size.width,), Positioned( left: 0, right: 0, bottom: 0, child: Container( padding: EdgeInsets.all(8), color: Color.fromARGB(10, 0, 0, 0), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text("这是图片上的文字描述", style: TextStyle(fontSize: 15, color: Colors.red),), IconButton( icon: Icon( Icons.favorite, color: _isFavor ? Colors.red : Colors.white, ), onPressed: () { setState(() { _isFavor = !_isFavor; }); }), ], ), ), ), ], ); } }



