Flutter 【容器布局组件(集合)】

容器与布局

通常的,把只包含单个子Widget的Widget称为容器,把可以包含多个子Widget的Widget称为布局,实际上容器本身也是一种布局,因为布局本质上是指的对控件的位置的管理或控制。

万能容器

Container

它可以为 Widget 添加大小、背景等各种参数,其 child 属性用于设置它的子控件。由于它较为复杂,这里简述一下它的绘制顺序

  1. 首先会绘制transform效果
  2. 接着绘制decoration
  3. 然后绘制child
  4. 最后绘制foregroundDecoration
属性类型简述
alignmentAlignmentGeometry容器内 child 的对齐方式
paddingEdgeInsetsGeometry容器内边距
colorColor容器的背景色
decorationDecoration容器的背景装饰
foregroundDecorationDecoration容器的前景装饰
widthdouble容器的宽度
heightdouble容器的高度
constraintsBoxConstraints容器的大小限制
marginEdgeInsetsGeometry容器外边距
transformMatrix4容器的变化
childWidget容器里显示的 Widget

Container 本身是一个盒子模型

图片[1]-Flutter 【容器布局组件(集合)】-趙哥博客-赵哥博客
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 简单总结

  1. 未设置约束和固定宽高时,若存在子控件,则原则上Container 和子控件一样大;不存在子控件,则原则上占满全屏。
  2. 若未设置约束和宽高,但设置了alignment属性,且存在子控件,那么Container 原则上也会占满全屏
  3. 设置了固定宽高,则无论是否存在子控件,宽高都是固定的。

关于几个属性的类型:

alignment可使用三种类型的值

  • Alignment
  • FractionalOffset
  • AlignmentDirectional

其中Alignment和FractionalOffset比较类似,而AlignmentDirectional主要用于国际化中左右顺序的支持。

marginpadding都使用相同类型的值,即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

属性类型简述
paddingEdgeInsetsGeometry容器内边距
childWidget容器里显示的 Widget

基础布局

弹性布局

Flex

弹性布局,类似于 CSS 的 Flexbox。

属性类型简述
directionAxis主轴的方向
mainAxisAlignmentMainAxisAlignment子Widget 在主轴的对齐方式
mainAxisSizeMainAxisSize主轴应该占用多大的空间
crossAxisAlignmentCrossAxisAlignment子Widget 在交叉轴的对齐方式
textDirectionTextDirection子Widget 在主轴方向上的布局顺序
verticalDirectionVerticalDirection子Widget 在交叉轴方向上的布局顺序
textBaselineTextBaseline子Widget 时使用哪个基线
childrenList< 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 【容器布局组件(集合)】-趙哥博客-赵哥博客

MainAxisAlignment.spaceAround

该效果我称为拉手布局,相当于小朋友伸开手,且相互间手拉手。

图片[3]-Flutter 【容器布局组件(集合)】-趙哥博客-赵哥博客

MainAxisAlignment.spaceEvenly

均分间距

图片[4]-Flutter 【容器布局组件(集合)】-趙哥博客-赵哥博客

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

水平方向的线性布局

属性类型简述
mainAxisAlignmentMainAxisAlignment子Widget 在主轴的对齐方式
mainAxisSizeMainAxisSize表示主轴应该占用多大的空间
crossAxisAlignmentCrossAxisAlignment子Widget 在交叉轴的对齐方式
textDirectionTextDirection子Widget 在主轴方向上的布局顺序
verticalDirectionVerticalDirection子Widget 在交叉轴方向上的布局顺序
textBaselineTextBaseline设置 子Widget 基线
childrenList< Widget>用于排列的子控件列表

Column

垂直方向的线性布局,其属性可直接参照Row

流式布局

Wrap

属性类型简述
directionAxis主轴的方向。默认是 Axis.horizontal
alignmentWrapAlignment子Widget 在主轴上的对齐方式,默认值为WrapAlignment.start
runAlignmentWrapAlignment纵轴对齐方式,默认值为WrapAlignment.start
runSpacingdouble纵轴间距,默认是0.0
crossAxisAlignmentWrapCrossAlignment交叉轴上的对齐方式
textDirectionTextDirection子Widget 在主轴方向上的排列顺序
verticalDirectionVerticalDirection子Widget 在交叉轴方向上的排列顺序
childrenList< Widget>子控件列表

层叠布局

Stack

属性类型简述
alignmentAlignmentDirectional决定子Widget如何对齐 ,默认值为 AlignmentDirectional.topStart
textDirectionTextDirection用于确定 alignment的对齐方向
fitStackFit决定非positioned子Widget 如何去适应Stack的大小
overflowOverflow如何显示超出 Stack空间的 子widget
childrenList< Widget>排列的子控件

在Stack布局中,通常会与另外两个控件配合使用,它们是AlignPositioned,前者用于相对定位,后者用于绝对定位。

Align比较简单,这里列一下Positioned的属性

属性类型简述
leftdouble离 Stack 左边的距离
topdouble离 Stack 上边的距离
rightdouble离 Stack 右边的距离
bottomdouble离 Stack 底边的距离
widthdouble设定子控件的宽度
heightdouble设定子控件的高度

辅助布局

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的builder
  • GridView.custom 用于构建自定义子Widget

GridView.count 是在交叉轴上创建固定个数的网格,crossAxisCount为必须的属性,表示交叉轴网格的个数,而GridView.extent是在交叉轴上创建最大可容纳的网格,maxCrossAxisExtent是必须的属性,表示交叉轴上网格的最大的宽度。

表格 Table/TableRow

表格布局和线性布局比较相似,只是使用起来更简洁一些。

属性类型简述
columnWidthsMap<int, TableColumnWidth>设置每一列的宽度
defaultColumnWidthTableColumnWidth默认的每一列宽度值,默认情况下均分
textDirectionTextDirection文字方向
borderTableBorder表格边框
defaultVerticalAlignmentTableCellVerticalAlignment每一个cell的垂直方向的alignment
childrenList<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;
    });
  }
}
THE END
喜欢就支持一下吧
点赞9赞赏 分享
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情代码图片

    暂无评论内容