容器与布局
通常的,把只包含单个子Widget的Widget称为容器,把可以包含多个子Widget的Widget称为布局,实际上容器本身也是一种布局,因为布局本质上是指的对控件的位置的管理或控制。
万能容器
Container
它可以为 Widget 添加大小、背景等各种参数,其 child
属性用于设置它的子控件。由于它较为复杂,这里简述一下它的绘制顺序
- 首先会绘制
transform
效果 - 接着绘制
decoration
- 然后绘制
child
- 最后绘制
foregroundDecoration
属性 | 类型 | 简述 |
---|---|---|
alignment | AlignmentGeometry | 容器内 child 的对齐方式 |
padding | EdgeInsetsGeometry | 容器内边距 |
color | Color | 容器的背景色 |
decoration | Decoration | 容器的背景装饰 |
foregroundDecoration | Decoration | 容器的前景装饰 |
width | double | 容器的宽度 |
height | double | 容器的高度 |
constraints | BoxConstraints | 容器的大小限制 |
margin | EdgeInsetsGeometry | 容器外边距 |
transform | Matrix4 | 容器的变化 |
child | Widget | 容器里显示的 Widget |
Container 本身是一个盒子模型
Container(
width: 300.0,
height: 500.0,
margin: EdgeInsets.all(16.0),
padding: EdgeInsets.all(16.0),
alignment: Alignment.center,
decoration: BoxDecoration(
//背景填充颜色
color: Colors.amberAccent,
//背景边框
border: Border.all(
//边框颜色
color: Colors.black12,
//边框宽度
width: 5),
//边框圆角
borderRadius: BorderRadius.only(
topLeft: Radius.circular(5.0),
topRight: Radius.circular(10.0),
bottomLeft: Radius.circular(15.0),
bottomRight: Radius.circular(20.0)),
//渐变效果,会覆盖 color
gradient: LinearGradient(
colors: [Colors.redAccent, Colors.greenAccent, Colors.blueAccent],
),
//阴影效果
boxShadow: [BoxShadow(color: Colors.grey, blurRadius: 5.0)],
),
//前景装饰,绘制在 child 之上
foregroundDecoration: BoxDecoration(
image: DecorationImage(image: NetworkImage('https://gitee.com/arcticfox1919/ImageHosting/raw/master/img/emm.jpg'))
))
Container 简单总结
- 未设置约束和固定宽高时,若存在子控件,则原则上
Container
和子控件一样大;不存在子控件,则原则上占满全屏。 - 若未设置约束和宽高,但设置了
alignment
属性,且存在子控件,那么Container 原则上也会占满全屏 - 设置了固定宽高,则无论是否存在子控件,宽高都是固定的。
关于几个属性的类型:
alignment
可使用三种类型的值
- Alignment
- FractionalOffset
- AlignmentDirectional
其中Alignment和FractionalOffset比较类似,而AlignmentDirectional主要用于国际化中左右顺序的支持。
margin
与padding
都使用相同类型的值,即EdgeInsets
,该类型共用四中构造方法
- EdgeInsets.all()
- EdgeInsets.symmetric()
- EdgeInsets.fromLTRB()
- EdgeInsets.only()
decoration
有四种类型,都是Decoration
的子类,其中三种在开发中会用到
- BoxDecoration
- ShapeDecoration
- UnderlineTabIndicator
ShapeDecoration 通常是用于单独为四条边框绘制不同的效果,它的属性与BoxDecoration大致相同,其中shape
属性是和BoxDecoration中不同的,类型为ShapeBorder
,它的取值如下
- Border 绘制四边
- UnderlineInputBorder 绘制底边线
- RoundedRectangleBorder 绘制矩形边框
- CircleBorder 绘制圆形边框
- StadiumBorder 绘制竖向椭圆边框
- BeveledRectangleBorder 绘制八角边框
边距容器
Padding
主要用于设置控件之间的边距,用法参见Container
属性 | 类型 | 简述 |
---|---|---|
padding | EdgeInsetsGeometry | 容器内边距 |
child | Widget | 容器里显示的 Widget |
基础布局
弹性布局
Flex
弹性布局,类似于 CSS 的 Flexbox。
属性 | 类型 | 简述 |
---|---|---|
direction | Axis | 主轴的方向 |
mainAxisAlignment | MainAxisAlignment | 子Widget 在主轴的对齐方式 |
mainAxisSize | MainAxisSize | 主轴应该占用多大的空间 |
crossAxisAlignment | CrossAxisAlignment | 子Widget 在交叉轴的对齐方式 |
textDirection | TextDirection | 子Widget 在主轴方向上的布局顺序 |
verticalDirection | VerticalDirection | 子Widget 在交叉轴方向上的布局顺序 |
textBaseline | TextBaseline | 子Widget 时使用哪个基线 |
children | List< Widget> | Flex布局里排列的子控件 |
direction
的取值:主轴的方向
Axis 值 | 简述 |
---|---|
Axis.horizontal | 主轴为水平方向,子Widget 就会沿水平方向排列,则交叉轴为垂直方向。 |
Axis.vertical | 主轴为垂直方向,子Widget 就会沿垂直方向排列,则交叉轴为水平方向。 |
mainAxisAlignment
的取值:子控件在主轴的对齐方式
MainAxisAlignment 值 | 简述 |
---|---|
MainAxisAlignment.start | 沿着主轴的起点对齐 textDirection 必须有值,以确定是从左边开始的还是从右边开始的 |
MainAxisAlignment.end | 沿着主轴的终点对齐 textDirection 必须有值,以确定是在左边结束的还是在右边结束的 |
MainAxisAlignment.center | 在主轴上居中对齐 |
MainAxisAlignment.spaceBetween | 在主轴上,两端对齐,子控件之间的间隔都相等 |
MainAxisAlignment.spaceAround | 在主轴上,将多余的控件均匀分布给 子控件之间,而且第一个 子Widget 和 最后一个子Widget 距边框的距离是 两个 子Widget 距离的一半 |
MainAxisAlignment.spaceEvenly | 在主轴上,将多余的控件均匀分布给子控件之间,而且第一个 子Widget 和 最后一个子Widget 距边框的距离和 子控件之间的距离一样 |
其中最后三个属性不太好理解,这里给出图示:
MainAxisAlignment.spaceBetween
两端顶格,中间均分
MainAxisAlignment.spaceAround
该效果我称为拉手布局,相当于小朋友伸开手,且相互间手拉手。
MainAxisAlignment.spaceEvenly
均分间距
mainAxisSize
的取值 : 表示主轴应该占用多大的空间
MainAxisSize 值 | 简述 |
---|---|
MainAxisSize.min | 主轴的大小是能显示完 子Widget 的最小大小,主轴的大小就是 子Widget 的大小 |
MainAxisSize.max | 主轴能显示的最大的大小,根据约束来判断 |
crossAxisAlignment
的取值:子控件在交叉轴的对齐方式
CrossAxisAlignment 值 | 简述 |
---|---|
CrossAxisAlignment.start | 沿着交叉轴的起点对齐 verticalDirection 必须有值,以确定是从左边开始的还是从右边开始的 |
CrossAxisAlignment.end | 沿着主轴的终点对齐 verticalDirection 必须有值,以确定是在左边结束的还是在右边结束的 |
CrossAxisAlignment.center | 在交叉轴上居中对齐 |
CrossAxisAlignment.stretch | 要求 子Widget 在交叉轴上填满 |
CrossAxisAlignment.baseline | 要求 子Widget 的基线在交叉轴上对齐 |
textDirection
的取值:子控件在主轴方向上的布局顺序
TextDirection 值 | 简述 |
---|---|
TextDirection.rtl | 表示从右到左 |
TextDirection.ltr | 表示从左到右 |
verticalDirection
的取值 :子控件在交叉轴方向上的布局顺序
VerticalDirection 值 | 简述 |
---|---|
VerticalDirection.up | 表示从下到上 |
VerticalDirection.down | 表示从上到下 |
线性布局
Row
水平方向的线性布局
属性 | 类型 | 简述 |
---|---|---|
mainAxisAlignment | MainAxisAlignment | 子Widget 在主轴的对齐方式 |
mainAxisSize | MainAxisSize | 表示主轴应该占用多大的空间 |
crossAxisAlignment | CrossAxisAlignment | 子Widget 在交叉轴的对齐方式 |
textDirection | TextDirection | 子Widget 在主轴方向上的布局顺序 |
verticalDirection | VerticalDirection | 子Widget 在交叉轴方向上的布局顺序 |
textBaseline | TextBaseline | 设置 子Widget 基线 |
children | List< Widget> | 用于排列的子控件列表 |
Column
垂直方向的线性布局,其属性可直接参照Row
流式布局
Wrap
属性 | 类型 | 简述 |
---|---|---|
direction | Axis | 主轴的方向。默认是 Axis.horizontal |
alignment | WrapAlignment | 子Widget 在主轴上的对齐方式,默认值为WrapAlignment.start |
runAlignment | WrapAlignment | 纵轴对齐方式,默认值为WrapAlignment.start |
runSpacing | double | 纵轴间距,默认是0.0 |
crossAxisAlignment | WrapCrossAlignment | 交叉轴上的对齐方式 |
textDirection | TextDirection | 子Widget 在主轴方向上的排列顺序 |
verticalDirection | VerticalDirection | 子Widget 在交叉轴方向上的排列顺序 |
children | List< Widget> | 子控件列表 |
层叠布局
Stack
属性 | 类型 | 简述 |
---|---|---|
alignment | AlignmentDirectional | 决定子Widget如何对齐 ,默认值为 AlignmentDirectional.topStart |
textDirection | TextDirection | 用于确定 alignment 的对齐方向 |
fit | StackFit | 决定非positioned子Widget 如何去适应Stack的大小 |
overflow | Overflow | 如何显示超出 Stack空间的 子widget |
children | List< Widget> | 排列的子控件 |
在Stack布局中,通常会与另外两个控件配合使用,它们是Align
和Positioned
,前者用于相对定位,后者用于绝对定位。
Align
比较简单,这里列一下Positioned
的属性
属性 | 类型 | 简述 |
---|---|---|
left | double | 离 Stack 左边的距离 |
top | double | 离 Stack 上边的距离 |
right | double | 离 Stack 右边的距离 |
bottom | double | 离 Stack 底边的距离 |
width | double | 设定子控件的宽度 |
height | double | 设定子控件的高度 |
辅助布局
Center
水平垂直居中布局。类似Container
设置alignment
SizedBox
固定宽高布局,类似Container
设置了宽高
AspectRatio
宽高比布局。
FractionallySizedBox
百分比布局。
Card
卡片布局。
高级布局
列表 ListView
它是一种可滚动的列表,共四种构造方法。其中最常用的是ListView.builder
构造方法,因为它适用于大量的列表项的情形,甚至可以是无限数量的项。
ListView()
ListView.builder()
ListView.separated()
ListView custom()
网格 GridView
用于创建二维网格列表。
GridView()
默认构造GridView.count
用于快速的创建固定横轴数量的网格GridView.extent
用于创建交叉轴子最大可容纳的网格GridView.builder
同ListView的builderGridView.custom
用于构建自定义子Widget
GridView.count
是在交叉轴上创建固定个数的网格,crossAxisCount
为必须的属性,表示交叉轴网格的个数,而GridView.extent
是在交叉轴上创建最大可容纳的网格,maxCrossAxisExtent
是必须的属性,表示交叉轴上网格的最大的宽度。
表格 Table/TableRow
表格布局和线性布局比较相似,只是使用起来更简洁一些。
属性 | 类型 | 简述 |
---|---|---|
columnWidths | Map<int, TableColumnWidth> | 设置每一列的宽度 |
defaultColumnWidth | TableColumnWidth | 默认的每一列宽度值,默认情况下均分 |
textDirection | TextDirection | 文字方向 |
border | TableBorder | 表格边框 |
defaultVerticalAlignment | TableCellVerticalAlignment | 每一个cell的垂直方向的alignment |
children | List<TableRow> | 子控件列表 |
示例
Container(
child: Table(
columnWidths: const {
//列宽
0: FixedColumnWidth(100.0),
1: FixedColumnWidth(200.0),
2: FixedColumnWidth(50.0),
},
border: TableBorder.all(
color: Colors.green,
width: 2.0,
style: BorderStyle.solid,
),
children: [
TableRow(
decoration: BoxDecoration(
color: Colors.grey,
),
children: [
SizedBox(
height: 30.0,
child: Text('姓名'),
),
Text('性别'),
Text('年龄'),
]
),
TableRow(
children: [
Text('张三'),
Text('男'),
Text('20'),
]
),
TableRow(
children: [
Text('李四'),
Text('女'),
Text('28'),
]
),
],
),
);
栈索引
IndexedStack 继承自Stack,用于显示第index个child
,而其他child
则是不可见的。所以IndexedStack的尺寸永远是跟最大的子控件尺寸一致。与Stack相比,只是多了index
的设置。
class _HomePageState extends State<HomePage> {
int _pageIndex = 0;
@override
Widget build(BuildContext context) {
print("_HomePageState build ...");
const bgColor = const [
Colors.red,
Colors.green,
Colors.blue,
Colors.yellow
];
return Scaffold(
appBar: AppBar(
title: Text("Flutter Widget"),
),
body: Container(
child: Column(
children: <Widget>[
Expanded(
child: IndexedStack(
index: _pageIndex,
children: <Widget>[
Container(
color: Colors.red,
),
Container(
color: Colors.green,
),
Container(
color: Colors.blue,
),
],
),
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
FlatButton(child: Text("1"),onPressed: ()=>onClick(0),),
FlatButton(child: Text("2"),onPressed: ()=>onClick(1)),
FlatButton(child: Text("3"),onPressed: ()=>onClick(2)),
],
)
],
),
),
);
}
void onClick(int index){
setState(() {
_pageIndex = index;
});
}
}