老孟里面控件非常全、可以去详细学习
一、线性布局Row 、Column
在Row和Column中有一个非常重要的概念:MainAxisAlignment(主轴)和CrossAxisAlignment(交叉轴),简单来说,MainAxisAlignment(主轴)就是与当前控件方向一致的轴,而CrossAxisAlignment(交叉轴)就是与当前控件方向垂直的轴,比如Row的主轴是水平方向,交叉轴是垂直方向,而Column的主轴是垂直方向,交叉轴是水平方向。
Row和Column是多子控件的容器类控件,Row控件水平布局,Column控件垂直布局。
1、主轴对齐方式
Row控件的主轴mainAxisAlignment
对齐方式默认值是MainAxisAlignment.start
,即子控件从开始处排列,这个开始处不一定是屏幕的左边,是从左到右还是从右到左排列取决于文本方向textDirection
属性,比如阿拉伯文本方向是从右到左的。
Row(
children: <Widget>[
Container(
height: 50,
width: 100,
color: Colors.red,
),
Container(
height: 50,
width: 100,
color: Colors.green,
),
Container(
height: 50,
width: 100,
color: Colors.blue,
),
],
)
效果如图:
黑色边框是Row控件的范围,默认情况下Row铺满父组件。主轴的对齐方式设置代码如下:
Row(
mainAxisAlignment: MainAxisAlignment.center,
...
)
主轴对齐方式有6种,效果如下图:
spaceAround和spaceEvenly区别是:
- spaceAround:第一个子控件距开始位置和最后一个子控件距结尾位置是其他子控件间距的一半。
- spaceEvenly:所有间距一样。
2、交叉轴对齐方式
和主轴相对应的就是交叉轴crossAxisAlignment
,交叉轴对齐方式默认是居中。Row控件的高度是依赖子控件高度,因此子控件高都一样时,Row的高和子控件高相同,此时是无法体现交叉轴对齐方式,修改3个颜色块高分别为50,100,150,这样Row的高是150,代码如下:
Row(
children: <Widget>[
Container(
height: 50,
width: 100,
color: Colors.red,
),
Container(
height: 100,
width: 100,
color: Colors.green,
),
Container(
height: 150,
width: 100,
color: Colors.blue,
),
],
)
效果如下:
交叉轴属性设置代码如下:
Row(
crossAxisAlignment: CrossAxisAlignment.center,
...
)
交叉轴对齐方式介绍如下:
CrossAxisAlignment.stretch
表示使子控件填满交叉轴。
3、主轴尺寸
主轴尺寸由mainAxisSize
属性控制,仅有min
和max
两种方式,默认是max
方法。min
表示尽可能小,而max
表示尽可能大,设置min
的代码如下:
Row(
mainAxisSize: MainAxisSize.min,
...
)
效果如下:
黑色边框是Row的边框。
二、Flex布局
Flex
组件可以沿着水平或垂直方向排列子组件,如果你知道主轴方向,使用Row
或Column
会方便一些,因为Row
和Column
都继承自Flex
,参数基本相同,所以能使用Flex的地方基本上都可以使用Row
或Column
。Flex
本身功能是很强大的,它也可以和Expanded
组件配合实现弹性布局。接下来我们只讨论Flex
和弹性布局相关的属性(其它属性已经在介绍Row
和Column
时介绍过了)。
Flex({
@required this.direction, //弹性布局的方向, Row默认为水平方向,Column默认为垂直方向
//direction: Axis.horizontal,
List<Widget> children = const <Widget>[],
})
Expanded、Flexible和Spacer都是具有权重属性的组件,可以控制Row、Column、Flex的子控件如何布局的控件。
Row(
children: <Widget>[
Container(
color: Colors.blue,
height: 50,
width: 100,
),
Flexible(
child: Container(
color: Colors.red,
height: 50,
)
),
Container(
color: Colors.blue,
height: 50,
width: 100,
),
],
)
效果如图:
还是有3个子控件,希望第一个占1/6,第二个占2/6,第三个占3/6,代码如下:
Column(
children: <Widget>[
Flexible(
flex: 1,
child: Container(
color: Colors.blue,
alignment: Alignment.center,
child: Text('1 Flex/ 6 Total',style: TextStyle(color: Colors.white),),
),
),
Flexible(
flex: 2,
child: Container(
color: Colors.red,
alignment: Alignment.center,
child: Text('2 Flex/ 6 Total',style: TextStyle(color: Colors.white),),
),
),
Flexible(
flex: 3,
child: Container(
color: Colors.green,
alignment: Alignment.center,
child: Text('3 Flex/ 6 Total',style: TextStyle(color: Colors.white),),
),
),
],
)
效果如图:
Spacer的通过Expanded的实现的,和Expanded的区别是:Expanded可以设置子控件,而Spacer的子控件尺寸是0,因此Spacer适用于撑开Row、Column、Flex的子控件的空隙,用法如下:
Row(
children: <Widget>[
Container(width: 100,height: 50,color: Colors.green,),
Spacer(flex: 2,),
Container(width: 100,height: 50,color: Colors.blue,),
Spacer(),
Container(width: 100,height: 50,color: Colors.red,),
],
)
效果如下:
总结如下:
- Spacer是通过Expanded来实现的,Expanded继承自Flexible。
- 填满剩余空间直接使用Expanded更方便。
- Spacer用于撑开Row、Column、Flex的子控件的空隙。
三、Stack、Positioned
Stack 这个是Flutter中布局用到的组件,跟Android中FrameLayout很像,都是可以叠加的现实View,具体的使用细节还是有些不同的,我们一一说来
Stack({
Key key,
this.alignment = AlignmentDirectional.topStart,
this.textDirection,
this.fit = StackFit.loose,
this.overflow = Overflow.clip,
List<Widget> children = const <Widget>[],
})
-
alignment : 指的是子Widget的对其方式,默认情况是以左上角为开始点 ,这个属性是最难理解的,它区分为使用了Positioned和未使用Positioned定义两种情况,没有使用Positioned情况还是比较好理解的,下面会详细讲解的
-
fit :用来决定没有Positioned方式时候子Widget的大小,StackFit.loose 指的是子Widget 多大就多大,StackFit.expand使子Widget的大小和父组件一样大
-
overflow :指子Widget 超出Stack时候如何显示,默认值是Overflow.clip,子Widget超出Stack会被截断,
Overflow.visible超出部分还会显示的
这些会是挺好懂的 主要介绍一下
Positioned
Positioned({
Key key,
this.left,
this.top,
this.right,
this.bottom,
this.width,
this.height,
@required Widget child,
})
left、top 、right、 bottom分别代表离Stack左、上、右、底四边的距离
class PositionScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Postion Title"),
),
body: Stack(
children: <Widget>[
Positioned(
left: 0,
top: 0,
right: 0,
bottom: 0,
child: Container(
color: Colors.black45,
),
),
Positioned(
top: 100.0,
left: 0,
right: 0,
child: Container(
color: Colors.blue,
child: Text("第一个组件"),
),
),
Positioned(
top: 200,
right: 100,
child: Container(
color: Colors.yellow,
child: Text("第二个组件"),
),
),
Positioned(
bottom: 0,
left: 0,
right: 0,
child: Container(
color: Colors.red,
child: Text("第三个组件"),
),
),
],
),
);
}
}
那么我们如果指定了left&&right&&top&bottom都是0的情况
那么这个组件就是和Stack大小一样填满它
IndexedStack
IndexedStack是Stack的子类,Stack是将所有的子组件叠加显示,而IndexedStack只显示指定的子组件,用法如下:
IndexedStack(
index: _index,
children: <Widget>[
Center(
child: Container(
height: 300,
width: 300,
color: Colors.red,
alignment: Alignment.center,
child: Icon(
Icons.fastfood,
size: 60,
color: Colors.blue,
),
),
),
Center(
child: Container(
height: 300,
width: 300,
color: Colors.green,
alignment: Alignment.center,
child: Icon(
Icons.cake,
size: 60,
color: Colors.blue,
),
),
),
Center(
child: Container(
height: 300,
width: 300,
color: Colors.yellow,
alignment: Alignment.center,
child: Icon(
Icons.local_cafe,
size: 60,
color: Colors.blue,
),
),
),
],
)
通过点击按钮更新_index
值,代码如下:
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
IconButton(
icon: Icon(Icons.fastfood),
onPressed: () {
setState(() {
_index = 0;
});
},
),
IconButton(
icon: Icon(Icons.cake),
onPressed: () {
setState(() {
_index = 1;
});
},
),
IconButton(
icon: Icon(Icons.local_cafe),
onPressed: () {
setState(() {
_index = 2;
});
},
),
],
)
效果如下: