容器与布局
通常的,把只包含单个子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 本身是一个盒子模型
![图片[1]-Flutter 【容器布局组件(集合)】-IT网络技术分享](https://gitee.com/arcticfox1919/ImageHosting/raw/master/img/box.png)
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
两端顶格,中间均分
![图片[2]-Flutter 【容器布局组件(集合)】-IT网络技术分享](https://gitee.com/arcticfox1919/ImageHosting/raw/master/img/Screenshot_1587064481.png)
MainAxisAlignment.spaceAround
该效果我称为拉手布局,相当于小朋友伸开手,且相互间手拉手。
![图片[3]-Flutter 【容器布局组件(集合)】-IT网络技术分享](https://gitee.com/arcticfox1919/ImageHosting/raw/master/img/Screenshot_1587064048.png)
MainAxisAlignment.spaceEvenly
均分间距
![图片[4]-Flutter 【容器布局组件(集合)】-IT网络技术分享](https://gitee.com/arcticfox1919/ImageHosting/raw/master/img/Screenshot_1587064808.png)
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;
    });
  }
}
                    





暂无评论内容