Flutter中widget(二)--多子元素布局

以下内容为自学笔记,若有幸被大神看到,望指正其不准,补充其不足。万分感谢!!!

文章目录

  • 一、简介
  • 二、Row和Column
    • (一)、概述
    • (二)、属性介绍
      • 1、mainAxisAlignment:
      • 2、mainAxisSize
      • 3、crossAxisAlignment
      • 4、textBaseline
      • 5、verticalDirection
      • 6、textDirection
      • 7、children
  • 三、Stack和IndexedStack
    • (一)概述
    • (二)Positioned定位widget
    • (三)Stack属性介绍
      • 1、alignment
      • 2、textDirection
      • 3、fit
      • 4、overflow
      • 5、children
    • (四)IndexedStack属性
      • 1、sizing
      • 2、index
  • 四、ListView
    • (一)概述
    • (二)List()
    • (三)ListView.builder()
    • (四)ListView.separated()
    • (五)ListView.custom()
  • 五、GridView
    • (一)概述
    • (二)构造函数
    • (三)gridDelegat参数
  • 六、ListBody
    • 1、概述
    • 2、构造函数
    • 3、使用方式
    • 4、效果
  • 七、Table表格布局
    • 1、概述
    • 2、构造函数
    • 3、使用
    • 4、效果
  • 八、Flow流式布局
  • 九、Wrap也是流式布局

一、简介

多子元素布局可以将多个子widget按一定的规则排列,实现项目中的需求。类似Android中的LinearLayoutRelativeLayoutFrameLayout等布局。

拥有多子元素的布局总体有10个,如下:

布局类 名称 描述
Row 水平布局 在水平(X轴)方向上排列子widget的列表。
Column 垂直布局 在垂直(Y轴)方向上排列子widget的列表。
Stack 堆叠布局 可以允许其子widget简单的堆叠(Z轴方向)在一起
IndexedStack 从一个子widget列表中显示单个孩子的Stack
Flow 流式布局 一个实现流式布局算法的widget
Table 表格布局 为其子widget使用表格布局算法的widget
Wrap 效果跟Flow相似。 可以在水平或垂直方向多行显示其子widget。
ListBody 一个widget,它沿着一个给定的轴,顺序排列它的子元素
ListView 滚动列表 可滚动的列表控件。ListView是最常用的滚动widget,
它在滚动方向上一个接一个地显示它的孩子。
在纵轴上,孩子们被要求填充ListView。
CustomMultiChildLayout 使用一个委托来对多个孩子进行设置大小和定位的小部件

二、Row和Column

(一)、概述

  1. **Row:**水平布局;在X轴方向上排列子widget的列表,相当于Android中的LinearLayout
  2. **Column:**垂直布局;在Y轴方向上排列子Widget的列表,相当于Android中的RelativeLayout
  3. 二者属性名称都是一样的,但根据各自身效果表现不同。
  4. 父类为:Flex,而Flex的父类为:MultiChildRenderObjectWidget
  5. :二者自身不带滚动属性,如果超出约束范围,在debug下会显示溢出的提示。

(二)、属性介绍

1、mainAxisAlignment:

  1. 主轴方向上的对齐方式,默认为MainAxisAlignment.start,取值为MainAxisAlignment的枚举值。
  2. Row:主轴方向为水平X轴。
  3. Column:主轴方向为垂直Y轴。

MainAxisAlignment的枚举值如下:

  • start:子widget在主轴的起点位置对齐;

    • Row:从左往右排列;

    • Column:从上往下排列。

  • end:子widget在主轴的末尾位置对齐;

    • Row:从右往左排列;
    • Column:从下往上排列。
  • center:子widget放置在主轴的中心;

  • spaceBetween:沿主轴方向,将空白区域(子widget的数量-1)均分,使得子widget之间的空白区域相等,但首尾无留白,子widget都靠近首尾对齐;

    new Row(
      mainAxisAlignment: MainAxisAlignment.spaceBetween,
      children: <Widget>[
        new Text('a裸奔的蚂蚁'),
        new Text('pabcpgj'),
        new Text('裸奔的蚂蚁j'),
      ],
    ),
    

    效果如下:
    Flutter中widget(二)--多子元素布局_第1张图片

  • spaceAround:沿主轴方向,将空白区域均分,使得子widget之间的空白区域相等,但首尾子widget临近边框的空白区域为1/2;

    new Row(
      mainAxisAlignment: MainAxisAlignment.spaceAround,
      children: <Widget>[...],
    ),
    

    效果如下:
    Flutter中widget(二)--多子元素布局_第2张图片

  • spaceEvenly:沿主轴方向,将空白区域(子widget的数量+1)等分,使得子widget之间的空白区域相等,首尾也留白;

    new Row(
      mainAxisAlignment: MainAxisAlignment.spaceEvenly,
      children: <Widget>[...],
    ),
    

    效果如下:
    Flutter中widget(二)--多子元素布局_第3张图片

2、mainAxisSize

  1. 主轴方向应占用多少空间,默认值MainAxisSize.max,取值范围为MainAxisSize的枚举值。
  2. MainAxisSize.max:最大化的占用主轴空间,最大约束值为父类的最大宽或高,但Row的最大不会无限增大,如果Row长度超过父类的宽(高)或超过屏幕边缘则会触发断言。
  3. MainAxisSize.min:根据子widget的长度适配占用主轴空间,但Row的长度不会无限增大,如果Row长度超过父类的宽(高)或超过屏幕边缘则会触发断言。
  4. 当设置MainAxisSize.min时;mainAxisAlignment中只有start生效。

使用方式:

new Row(
  mainAxisAlignment: MainAxisAlignment.spaceEvenly,
  mainAxisSize: MainAxisSize.man,
  children: <Widget>[...],
),

3、crossAxisAlignment

  1. 交叉轴方向上的对齐方式,默认是CrossAxisAlignment.center,取值为CrossAxisAlignment的枚举值。
  2. Row的交叉轴:为垂直轴(Y轴);
  3. Column的交叉轴:为水平轴(X轴);

CrossAxisAlignment的枚举值如下:

  • start:子widget在交叉轴起点位置对齐;

    new Container(width: 400.0,height:400.0,
       child: new Row(
       crossAxisAlignment: CrossAxisAlignment.start,
       children: <Widget>[
           new Text('1裸奔的蚂蚁'),
           new Text('pabcpgj'),
           new Text('2裸奔的蚂蚁'),
           new Container(
               color: Colors.lightBlue, 
               width: 80.0,
               height: 80.0,
           ),
       	],
       )),
    //-----------------------------Column------------------------------------------------
    new Container(width: 400.0,height:400.0,
       child: new Column(
       crossAxisAlignment: CrossAxisAlignment.start,
       children: <Widget>[...],)),
    

    效果如下:
    Flutter中widget(二)--多子元素布局_第4张图片

  • end:子widget在交叉轴末尾位置对齐;

    new Container( width: 400.0,height:400.0,
       child: new Row(
         crossAxisAlignment: CrossAxisAlignment.end,
         children: <Widget>[同上],
       )),
    //-------------------------------Column--------------------------------------------
    new Container(width: 400.0,height:400.0,
       child: new Column(
         crossAxisAlignment: CrossAxisAlignment.end,
         children: <Widget>[同上],
       )),
    

    效果如下:
    Flutter中widget(二)--多子元素布局_第5张图片

  • center:子widget在交叉轴居中展示;

    new Container(color: Colors.red,width: 400.0,height:400.0,
       child: new Row(
         crossAxisAlignment: CrossAxisAlignment.center,
         children: <Widget>[同上],
       )),
    //-------------------------------Column--------------------------------------------
    new Container(color: Colors.red,width: 400.0,height:400.0,
       child: new Column(
         crossAxisAlignment: CrossAxisAlignment.center,
         children: <Widget>[同上],
       )),
    

    效果如下:
    Flutter中widget(二)--多子元素布局_第6张图片

  • stretch:让子widget填满交叉轴方向;

    new Container(color: Colors.red,width: 400.0,height:400.0,
       child: new Row(
         crossAxisAlignment: CrossAxisAlignment.stretch,
         children: <Widget>[同上],
       )),
    //-------------------------------Column--------------------------------------------
    new Container(color: Colors.red,width: 400.0,height:400.0,
       child: new Column(
         crossAxisAlignment: CrossAxisAlignment.stretch,
         children: <Widget>[同上],
       )),
    

    效果如下:
    Flutter中widget(二)--多子元素布局_第7张图片

  • baseline:子widget之间,在交叉轴方向上,是它们的基线适配

    • 此属性不能单独使用,要与textBaseline属性配合使用,否则会报错,示例看textBaseline内;
    • 此值在Column中,作用相当于start,因为基线总是水平的;

4、textBaseline

  1. 描述文本内容的基线类型,取值为TextBaseline的枚举值;

  2. 此属性在Row中与CeossAxisAlignment.baseline配合使用,才能看出效果;

    • alphabetic:以字母字符底部的水平线对齐;
    • ideographic:以表意字符的水平线对齐。
    new Row(
      crossAxisAlignment: CrossAxisAlignment.baseline,
      textBaseline: TextBaseline.alphabetic,
      children: <Widget>[...],
    ),
    //-----------------------ideographic------------------------------
    new Row(
      crossAxisAlignment: CrossAxisAlignment.baseline,
      textBaseline: TextBaseline.ideographic,
      children: <Widget>[...],
    ),
    

    效果如下:
    Flutter中widget(二)--多子元素布局_第8张图片

5、verticalDirection

  1. 定义了子widget的在垂直方向上的排列顺序,并影响垂直方向的对齐方式,默认值为VerticalDirection.down

  2. 取值为VerticalDirection内的枚举值,如下:

    • up:子widget从bottom开始往top排列;
    • down:子widget从top开始往bottom排列。
  3. 由于只作用在垂直方向,所以在RowColumn中作用效果不同:

    • Row:对应crossAxisAlignmentstartend属性,由于子widget为水平排列,所以不影响排列顺序,但会影响垂直方向的对齐方式
    • Column:对应mainAxisAlignmentstartend属性,影响子widget元素的排列顺序,并且会影响垂直方向的对齐方式

使用方式:

new Row(
  crossAxisAlignment: CrossAxisAlignment.start,
  verticalDirection: VerticalDirection.up,
  children: <Widget>[...],
),
//---------------Column----------------------------
new Column(
  mainAxisAlignment: MainAxisAlignment.end,
  verticalDirection: VerticalDirection.down,
  children: <Widget>[...],
),

Row的效果:
Flutter中widget(二)--多子元素布局_第9张图片
Column的效果:
Flutter中widget(二)--多子元素布局_第10张图片

6、textDirection

  1. verticalDirection类似,只是定义的子widget在水平方向上的排列顺序,并影响水平方向的对齐方式;
  2. 取值为TextDirection的枚举值包含:
    • ltr:left to right,从左到右排列;
    • rtl:rigth to left,从右到左排列;
  3. 由于只作用在水平方向,所以在RowColumn中作用效果不同:
    • Row:对应mainAxisAlignmentstartend属性,影响影响子widget元素排列顺序,并影响水平方向的对齐方式
    • Column:对应crossAxisAlignmentstartend属性,由于子widget为垂直排列,所以不影响排列顺序,但会影响水平方向的对齐方式

使用方式:

new Row(
  mainAxisAlignment: CrossAxisAlignment.start,
  textDirection: TextDirection.rtl,
  children: <Widget>[...],
),
//---------------Column----------------------------
new Column(
  crossAxisAlignment: MainAxisAlignment.end,
  textDirection: TextDirection.rtl,
  children: <Widget>[...],
),

Row的效果:
Flutter中widget(二)--多子元素布局_第11张图片
Column的效果:
Flutter中widget(二)--多子元素布局_第12张图片

7、children

是一个Widget类型的list集合,是填充布局的元素。

三、Stack和IndexedStack

(一)概述

  1. Stack:叠加布局,即在Z轴方向上叠加子widget列表。类似Android中的FrameLayout;
  2. Stack的父类为:MultiChildRenderObjectWidget
  3. IndexedStack:是Stack的子类,可指定显示子widget列表中的一个子widget。
  4. 由于是继承关系,基本属性都一样,唯一的区别是IndexedStack中多一个index属性指定显示子widget列表中的哪个widget。
  5. 通常使用Positioned来对子widget元素来定位,建议在三个以上子widget元素时使用;

(二)Positioned定位widget

辅助Stack内子widget元素定位。有以下属性:

  • width:子widget元素的宽。
  • left:子元素距离左边的距离。
  • right:子元素距右边的距离。
  • height:子widget元素的高。
  • top:子元素距离顶部的距离。
  • bottom:子元素距离底部的距离。

注:

  • widthleftright三个属性不能同时使用,必须至少有一个属性是null;
  • heighttopbottom三个属性不能同时使用,必须至少有一个属性是null。

(三)Stack属性介绍

1、alignment

  1. 叠加对齐方式,默认是AlignmentDirectional.topStart左上对齐叠加;

  2. 详情请移步单子元素布局中Container的alignment

    • AlignmentDirectional
      • 默认使用此类,带方向性的对齐方式;
      • 此对齐方式,带有方向性,能与textDirction配合使用改变对齐方式。
      • 常量成员有9个:topStarttopCentertopEndcenterStartcentercenterEndbottomStartbottomCenterbottomEnd,start表示左边,end表示右边。
    • Alignment
      • 固定位置对齐方式,不带有方向性;
      • 由于不带方向性,属性textDirction没有效果
    • FractionalOffset
      • 使用与Alignment一样,不同点就是讲坐标偏移了,使用构造函数时传入坐标点要注意。

    使用方法:

    new Stack(
      //下图为九个不同的对齐方式,使用构造方法参数为double类型
      alignment: Alignment(-1.0,0.0),//等价于Alignment.centerLeft
      children: <Widget>[
        Positioned(child: Container(width: 100.0,height: 100.0,color: Colors.red,),),
        Positioned(child: Container(width: 90.0,height: 90.0,color: Colors.green,),),
        Positioned(child: Container(width: 80.0,height: 80.0,color: Colors.blue,),),
        new Text('-1,0\ncenterLeft',textAlign: TextAlign.left,),
      ],
    ),
    

    Flutter内的坐标图:每个小方块为一个Stack其在内部坐标也是一样的。
    Flutter中widget(二)--多子元素布局_第13张图片

2、textDirection

  1. AlignmentDirectional.xxxStart(或xxxend)配合使用可以改变对齐方式。
  2. 取值为TextDirction中的枚举值ltrrtl,使用方式和效果与Row类似。

3、fit

  1. 调整子widget集合中未定位元素(可使用辅助组件Positioned定位)的大小;

  2. 默认值为StackFit.loose,取值为StackFit中的枚举值。如下:一般使用前两个

    • loose:从Stack传递过来的约束被解除,即:

      当Stack大小被限制为300*400,子widget元素的大小将可设置为,宽:0到300,高:0到400。

    • expand:从Stack传递过来的约束被锁定到最大,即:

      当Stack大小被限制为300*400,子widget元素的大小将锁定为,宽:300,高:400。

    • passthrough:不改变子组件约束条件。效果目测和loose一样

      官方解释:

      The constraints passed to the stack from its parent are passed unmodified to the non-positioned children.

      For example, if a [Stack] is an [Expanded] child of a [Row], the horizontal constraints will be tight and the vertical constraints will be loose.

使用方法

new Container(
   color: Color(0xFFE0E0E0),
   alignment: Alignment.center,
   width: 110.0,
   height: 110.0,
   margin: EdgeInsets.all(10.0),
   child: new Stack(
     fit: StackFit.loose,
   //fit: StackFit.expand, //第二个图
     children: <Widget>[
      Positioned(child: Container(width: 100.0,height: 100.0,color: Colors.red,),),
      Positioned(child: Container(width: 90.0,height: 90.0,color: Colors.green,),),
      Positioned(child: Container(width: 80.0,height: 80.0,color: Colors.blue,),),
    ],
  ),
),

效果如下:
Flutter中widget(二)--多子元素布局_第14张图片

4、overflow

  1. 是否裁剪溢出的子widget元素,默认值为Overflow.clip
  2. 取值为Overflow的枚举值,如下:
    • clip:裁剪溢出部分
    • visible:显示溢出部分

使用方法:

new Container(
   .....
   child: new Stack(
     overflow: Overflow.clip,
   //overflow: Overflow.visible,
     children: <Widget>[
       Positioned(child: Container(width: 100.0,height: 100.0,color: Colors.red,),),
       Positioned(child: Container(width: 130.0,height: 90.0,color: Colors.green,),),
       Positioned(child: Container(width: 80.0,height: 120.0,color: Colors.blue,),),
     ],
   ),
),

效果如图:
Flutter中widget(二)--多子元素布局_第15张图片

5、children

是一个Widget类型的list集合,是填充布局的元素。

(四)IndexedStack属性

1、sizing

  1. 此属性默认为StackFit.loose,构造函数传递给父类中fit属性。
  2. 但此属性设置并未有父类的效果,三个参数的效果都是loose是效果。

2、index

  1. indexedStack特有属性,用来指定显示那个子widget元素;
  2. index取值为:子widget集合的索引,0到children.length-1。

使用方式:

new IndexedStack(
  sizing: StackFit.expand,
  index: 1,
  children: <Widget>[
    Positioned(child: Container(width: 100.0,height: 100.0,color: Colors.red,),),
    Positioned(child: Container(width: 130.0,height: 90.0,color: Colors.green,),),
    Positioned(child: Container(width: 80.0,height: 120.0,color: Colors.blue,),),
   ],
),

效果如下:只显示第二个元素
Flutter中widget(二)--多子元素布局_第16张图片

四、ListView

(一)概述

  1. 线性排列的可以滚动的widget列表,类似Android的ListVIewRecycleView
  2. GridView相似,都是继承自BoxScrollView,区别是GridView二维的,类似网格状。
  3. ListView是在主轴方向可以滚动,在交叉轴方向,则是填满。
  4. Flutter中提供4中构造方法:
    • List():默认构造;
    • ListView.builder();
    • ListView.separated
    • ListView.custom
  5. 属性列表:
序号 字段 类型 作用
1 scrollDirection Axis 主轴滚动方向;默认垂直滚动Axis.vertical,还可水平滚动Axis.horizontal
2 reverse bool 是否反向排列,默认false,即正序排列
3 controller ScrollController 滚动控制器,默认为null
4 primary bool 是否强制滚动,默认false
5 physics ScrollPhysics 响应用户滑动手势;默认为false,即到底部或顶部,继续滑则不可继续滚动
6 shrinkWrap bool 滚动视图在[滚动方向]中的范围是否应由正在查看的内容决定。默认为false
7 padding EdgeInsetsGeometry 距边框item边间距
8 cacheExtent double 缓存大小,默认250.0
9 itemExtent double item在交叉轴方向上的长度;null时为自适应
10 addAutomaticKeepAlives bool 是否自动保存滑出屏幕外的字widget的状态,默认true,保存,可复用
11 addRepaintBoundaries bool 是否放置到重绘列表中;默认true,可提高性能
12 itemBuilder IndexedWidgetBuilder builderseparated构造函数特有要传入参数,定义item的样式
13 separatorBuilder IndexedWidgetBuilder separated构造函数特有要传入的参数,定义分割线的样式
14 itemCount int 列表可滚动的item数量,默认构造没有
15 childrenDelegate SliverChildDelegate 内部都是靠此类实现滚动和布局,但只有custom构造函数需要传入,其余都内部实现

(二)List()

  1. 默认构造,使用与内容较少的情形,因为它是把数据直接放到列表,一次性渲染所以内容;当item数目过多时,容易出现卡顿现象,导致滑动不畅;

  2. 如果需要设置分割线,则需要对列表item添加处理,在item之间(在奇数位)加一个分割线widget:Divider()

  3. children属性中直接布局。

  4. 参数介绍

    • scrollDirection:主轴滚动方向
      • 默认为垂直:Axis.vertical
      • 值为Axis的枚举值:垂直vertical,竖直horizontal 。
    • reverse:是否反向,
    • 默认为false,正常顺序从起始点开始正序;true:从末尾开始排列
    • controller:滚动控制器,默认为null
    • primary:是否强制滚动(到达顶部或底部时是否可以滚动)
      • 默认是false,如果为true,controller必须为null
    • physics:视图如何响应用户的手势滑动 ,可忽略primary属性
      • 有内置实现,强制可以滚动const AlwaysScrollableScrollPhysics();
      • 强制不可以滚动const ScrollPhysics();
    • shrinkWrap:滚动视图在[滚动方向]中的范围是否应由正在查看的内容决定
      • 默认为false。
    • itemExtent:item交叉轴方向的大小
      • ,默认自适应,vertical时为高度,horizontal时为高度
    • addAutomaticKeepAlives:是否自动保存滑出屏幕外的字widget的状态
      • 默认为true,保证widget不被回收,可复用,false的手动保存。
    • addRepaintBoundaries: 是否放置到重绘列表中
      • 默认为true,复杂widget可提高性能。
    • cacheExtent:缓存区大小,默认为250。
    • children:widget集合
    • 默认构造函数中特有,直接将子widget放置内,一次性渲染完成,适合少量数据

使用方式:

ListView(
    //主轴滚动方向:垂直vertical,竖直horizontal ,默认为垂直vertical,
  scrollDirection: Axis.vertical,
    //是否反向 默认为false,正常顺序从起始点开始正序,true为从末尾开始排列
  reverse: false,
    //滚动控制器,默认为null
  controller:null,
    //是否强制滚动(顶部或底部时是否可以滚动),默认是false,如果为true,Controller必须为null
  primary: false,
    //视图如何响应用户的手势滑动,有内置实现强制可以滚动 const AlwaysScrollableScrollPhysics();和强制不可以滚动const ScrollPhysics(),可忽略primary属性 
  physics: const AlwaysScrollableScrollPhysics(),
    //默认为false,滚动视图在[滚动方向]中的范围是否应由正在查看的内容决定。
  shrinkWrap: false,
    //item的padding值
  padding: const EdgeInsets.all(10.0),
    //item交叉轴方向的大小,默认自适应,vertical时为高度,horizontal时为高度
  itemExtent: 50.0,
    //是否自动保存滑出屏幕外的字widget的状态,保证widget不被回收,可复用,false的手动保存。默认为true
  addAutomaticKeepAlives: true,
    //是否放置到重绘列表中,复杂widget可提高性能,默认为true
  addRepaintBoundaries: true,
    //缓存区大小,默认为250
  cacheExtent: 250.0,
    //默认构造函数中特有,直接将子widget放置内,一次性渲染完成,适合少量数据
  children: [ 
    new Text('裸奔的蚂蚁1'),
    Divider(),
    new Text('裸奔的蚂蚁2'),
    Divider(),
    new Text('裸奔的蚂蚁3'),
    Divider(),
    new Text('裸奔的蚂蚁4'),
    Divider(),
    new Text('裸奔的蚂蚁5'),
  ],
),

(三)ListView.builder()

  1. 此构造函数属于懒加载,设置单个item的属性,适用于具有大量(或无限)子元素的列表视图,初始渲染时并不会所有都渲染,而只会渲染可见的条目。
  2. 此构造方法与默认方法基本相同,只是用itemBuilderitemCount替换了默认构造函数中的children
  3. itemBuilder为必写属性,此属性定义item的样式。
  4. itemCount列表可滚动的长度,不设置为无限滚动,。

使用方式:

final List<String> name = <String>['裸奔的蚂蚁1','裸奔的蚂蚁2',...,'裸奔的蚂蚁36',];
ListView.builder(
  itemCount: name.length * 2,//列表可滚动的长度,可指定固定数据的长度
  itemBuilder: (context, index) {
    if (index.isOdd) {//如果为奇数位置添加一个分割线,也占用一个item的位置
      return Divider(); //可自定义
    }
    index ~/= 2;  //除以向下取整
    return Text(name[index]);
  },
),

(四)ListView.separated()

  1. 这是一个自带分割线的item,与builder构造类似,又多了一个参数separatorBuilder用于控制列表各个元素的间隔如何渲染。

  2. 此构造函数中itemBuilderseparatorBuilderitemCount三个参数为必写参数。

  3. 内部实现类似上个代码中分割线处理

    return index.isEven
        ? itemBuilder(context, itemIndex)
        : separatorBuilder(context, itemIndex);
    

使用方式:

ListView.separated(
  itemBuilder: (context, index) {
    return Text(name[index]);
  },
  separatorBuilder: (context, index) {
    return Divider();//可自定义
  },
  itemCount: name.length,
),

(五)ListView.custom()

  1. 可自定义构造函数,必须的参数为childrenDelegate然后传入一个实现了SliverChildDelegate的组件,如下,
  2. 系统提供两个实现类:
    • SliverChildListDelegate:默认构造使用此实现类;
    • SliverChildBuilderDelegate:builder和separated使用此实现类。

使用方法:

ListView.custom(
  childrenDelegate: SliverChildBuilderDelegate(
    //内部自定义
    (context, index) {
      return index.isEven? Text(name[index ~/ 2]):Divider();
    },
    childCount: name.length * 2 - 1,
    addAutomaticKeepAlives: true,
    addRepaintBoundaries: true,
  ),
),

五、GridView

(一)概述

  1. 一个二维的可滚动的widget的数组布局。类似Android的GridView效果。

  2. 参数和ListView一样,唯一的区别是多了交叉轴方向的参数gridDelegate,来控制交叉轴。

  3. 效果:
    Flutter中widget(二)--多子元素布局_第17张图片

  4. 提供了5个构造函数,使用和ListView一样:

    • GridView()
    • GridVIew.builder()
    • GridView.costom
    • GirdView.count()
    • GridView.extent()

(二)构造函数

提供了5个构造函数,使用和ListView一样:

  • GridView()
    • 默认构造函数,固定item数量的布局;少量数据可使用
    • 特有参数children
GridView(
  gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
    crossAxisCount: 3,//交叉轴方向的数目
  ),
  children: <Widget>[Text('裸奔的蚂蚁1'),Text('裸奔的蚂蚁2'),Text('裸奔的蚂蚁3'),
    Text('裸奔的蚂蚁4'),Text('裸奔的蚂蚁5'),Text('裸奔的蚂蚁6'),Text('裸奔的蚂蚁7'),
    Text('裸奔的蚂蚁8'),Text('裸奔的蚂蚁9'),],
),
  • GridVIew.builder()
    • 此构造函数属于懒加载,适用于具有大量(或无限)子元素的列表视图,
    • 必传参数itemBuildergridDelegate
final List<String> name = <String>['裸奔的蚂蚁1','裸奔的蚂蚁2',...,'裸奔的蚂蚁36',];
GridView.builder(
  itemCount: name.length,
  gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
    crossAxisCount: 3,//交叉轴方向item的数量
    mainAxisSpacing: 2.0,//主轴方向的间隔
    crossAxisSpacing: 2.0,//交叉轴之间的间隔
  ),
  itemBuilder: (context, index) {//item布局样式
    return Container(
      alignment: Alignment.center,
      color: Colors.cyanAccent,
      child: Text(name[index]),
    );
  },
),
  • GridView.costom
    • 可自定义构造函数,
    • 必须参入参数childrenDelegategridDelegate
 GridView.custom(
    gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
      maxCrossAxisExtent: 80.0,//在交叉轴方向上单个item的最大长度
      crossAxisCount: 3,//交叉轴方向item的数量
	  mainAxisSpacing: 2.0,//主轴方向的间隔
    ),
    childrenDelegate: SliverChildBuilderDelegate(//item的样式
      (context, index) {
        return Container(
          alignment: Alignment.center,
          color: Colors.cyanAccent,
          child: Text(name[index]),
        );
      },
      childCount: name.length,//列表可滚动的item的数量
    ),
),
  • GirdView.count()
    • 创建一个在交叉轴方向由固定数目的可滚动的二维组件列表,
    • 适用少量数据
    • 必传参数crossAxisCount在交叉轴方向的数目
GridView.count(
  crossAxisCount: 3,
  children: <Widget>[Text('裸奔的蚂蚁1'),Text('裸奔的蚂蚁2'),Text('裸奔的蚂蚁3'),
    Text('裸奔的蚂蚁4'),Text('裸奔的蚂蚁5'),Text('裸奔的蚂蚁6'),Text('裸奔的蚂蚁7'),
    Text('裸奔的蚂蚁8'),Text('裸奔的蚂蚁9'),],
),
  • GridView.extent()
    • 创建一个根据单个item在交叉轴方向的长度平分并占满交叉轴的可滚动的二维组件
    • 适用少量数据
    • 必传参数maxCrossAxisExtent:交叉轴方向上单个item的大小
GridView.extent(
  maxCrossAxisExtent: 100.0,//在交叉轴方向上item的最大长度
  crossAxisCount: 3,//交叉轴方向item的数量
  mainAxisSpacing: 2.0,//主轴方向的间隔
  children: <Widget>[
    Container(color: Colors.cyanAccent,child: Text('裸奔的蚂蚁1'),),
    Container(color: Colors.cyanAccent,child: Text('裸奔的蚂蚁2'),),
    Container(color: Colors.cyanAccent,child: Text('裸奔的蚂蚁3'),),
    Container(color: Colors.cyanAccent,child: Text('裸奔的蚂蚁4'),),
    Container(color: Colors.cyanAccent,child: Text('裸奔的蚂蚁5'),),
    Container(color: Colors.cyanAccent,child: Text('裸奔的蚂蚁6'),),
    Container(color: Colors.cyanAccent,child: Text('裸奔的蚂蚁7'),),
    Container(color: Colors.cyanAccent,child: Text('裸奔的蚂蚁8'),),
    Container(color: Colors.cyanAccent,child: Text('裸奔的蚂蚁9'),),
  ],
),

(三)gridDelegat参数

此参数为控制交叉轴方向item布局的参数,传入SliverGridDelegate的实现类

系统提供两个实现类,如下:

  • SliverGridDelegateWithFixedCrossAxisCount

    count构造函数内部使用此类,此类可以设置交叉轴方向item的个数

    参数如下:

    crossAxisCount,//交叉轴上item的个数
    mainAxisSpacing = 0.0,//主轴方向上的item的间距
    crossAxisSpacing = 0.0,//交叉轴方向上item的间距
    childAspectRatio = 1.0,//item交叉轴与主轴方向长度的比例
    
  • SliverGridDelegateWithMaxCrossAxisExtent:

    extent构造函数内部使用此类,此类将最大限度的占用交叉轴的最大长度来布局item

    参数如下:

    maxCrossAxisExtent,//交叉轴上item的最大长度,
    				//比如:屏幕宽500,此参数150,则将在交叉轴上布局4个125的item
    mainAxisSpacing = 0.0,//主轴方向上的item的间距
    crossAxisSpacing = 0.0,//交叉轴方向上item的间距
    childAspectRatio = 1.0,//item交叉轴与主轴方向长度的比例
    

六、ListBody

1、概述

  1. 这是一个按照指定轴方向排列子widget的布局;
  2. 很少单独使用,与可以指定主轴方向的控件一起使用,如RowColumnListViewFlex等。
  3. 参数介绍:
    • mainAxis:排列的主轴方向
    • reverse:是否反向
    • children:子widget的集合
  4. 功能和RowColumn类似,通常很少使用吃控件。
  5. 父类为:MultiChildRenderObjectWidget

2、构造函数

ListBody({Key key,
  //排列的主轴方向,默认为垂直,
  //不可为空,并且与父布局主轴方向一致   
  this.mainAxis = Axis.vertical,
  this.reverse = false,//是否反向
  List<Widget> children = const <Widget>[],
}) : assert(mainAxis != null),
     super(key: key, children: children);

3、使用方式

Column(
  //主轴垂直排列的列表,未限制宽,默认将充满屏幕
  children: <Widget>[
    ListBody(
      //指定主轴方向与父框架相同
      mainAxis: Axis.vertical,
      reverse: false,//不反向
      children: <Widget>[
        Container(color: Colors.red, width: 50.0, height: 50.0),
        Container(color: Colors.yellow, width: 50.0, height: 50.0),
        Container(color: Colors.green, width: 50.0, height: 50.0),
        Container(color: Colors.blue, width: 50.0, height: 50.0),
        Container(color: Colors.black, width: 50.0, height: 50.0),
      ],
    )
  ],
)

4、效果

Flutter中widget(二)--多子元素布局_第18张图片

七、Table表格布局

1、概述

  • 创建一个表格的布局
  • 父类为:RenderObjectWidget
  • 表格的每一行的高度,由其内容决定,每一列的宽度,则由columnWidths属性单独控制。

2、构造函数

Table({Key key,
   this.children = const <TableRow>[],
   this.columnWidths,//
   this.defaultColumnWidth = const FlexColumnWidth(1.0),
   this.textDirection,
   this.border,
   this.defaultVerticalAlignment = TableCellVerticalAlignment.top,
   this.textBaseline,
 })
  • 参数介绍
名称 类型 作用
columnWidths Map 每列单元格的宽度,int为从0到每行的个数-1
defaultColumnWidth TableColumnWidth 默认的每一列宽度值,默认情况下均分。
textDirection TextDirection 每列的排列方向,默认从左到右
border TableBorder 表格的边框
defaultVerticalAlignment TableCellVerticalAlignment 单元格默认垂直方向上的对齐方式,默认上对齐TableCellVerticalAlignment.top
textBaseline TextBaseline TableCellVerticalAlignment.baseline与此属性配合使用。文本基线类型
children List 存放每行的单元格内容的类别,
  • TableCellVerticalAlignment包含枚举值如下:

    • top:被放置在单元格的顶部;
    • middle1:在单元格内垂直居中;
    • bottom:放置在单元格底部;
    • baseline:与文本基线对齐
    • fill:充满整个单元格。
  • TableRow,每行单元格布局与内容

    • 构造函数

      const TableRow({ this.key, 
         this.decoration, //装饰描述,即对表格每行背景设置,详情看单子元素中Container
         this.children //每行的内容
      });
      

3、使用

Container(
  width: 300.0,height: 200.0,
  padding: EdgeInsets.all(2.0),
  color: Color(0xFFC5CAE9),
  child: Table(
    //每行中单元格的宽度,TableRow内元素个数,即列数,从第一个到最后一个的宽度
    //如果排列根据排列方向显示不同
    columnWidths: const <int, TableColumnWidth>{
      0: FixedColumnWidth(30.0),
      1: FixedColumnWidth(70.0),
      2: FixedColumnWidth(50.0),
      3: FixedColumnWidth(100.0),
    },
    //默认未显示宽度  默认的每一列宽度值,默认情况下均分。
    defaultColumnWidth: const FlexColumnWidth(1.0),
    //每个表格的排列方向,此处设置从右到左
    textDirection: TextDirection.rtl,
    //表格边框,此处设置蓝色,2像素宽,实线
    border: TableBorder.all(color: Colors.blue, width: 2.0, style: BorderStyle.solid),
    //每一个单元格的垂直方向的对齐方式,默认为顶部对齐
    defaultVerticalAlignment: TableCellVerticalAlignment.top,
    //基线类型,与TableCellVerticalAlignment.baseline一起使用
    //textBaseline: null,
    children: <TableRow>[
      TableRow(
        decoration: BoxDecoration(color: Colors.purpleAccent),
        children: <Widget>[
          Text('A1'),Text('A2'),Text('A3'),Text('A4'),
        ],
      ),
      TableRow(
        decoration: BoxDecoration(color: Colors.purpleAccent),
        children: <Widget>[
          Container(color: Colors.red, child: Text('赤')),
          Container(color: Colors.orange, child: Text('橙')),
          Container(color: Colors.yellow, child: Text('黄')),
          Container(color: Colors.green, child: Text('绿')),
        ],
      ),
      TableRow(children: <Widget>[
        Text('B1'),Text('B2'),Text('B3'),Text('B4'),
      ]),
    ],
  ),
)

4、效果

Flutter中widget(二)--多子元素布局_第19张图片

八、Flow流式布局

  • 根据自定义FlowDelegate这个代理,有效地调整子组件的大小和位置的小部件。

  • Flow在用转换矩阵(transformation matrices)对child进行位置调整的时候进行了优化。

  • Flow以及其child的一些约束都会受到FlowDelegate的控制,例如重写FlowDelegate中的geiSize(),可以设置Flow的尺寸,重写其getConstraintsForChild(方法,可以设置每个child的布局约束条件)。

  • Flow之所以高效,是因为其在定位过后,如果使用FlowDelegate中的paintChildren()改变child的尺寸或者位置,只是重绘,并没有实际调整其位置

  • 抽象类FlowDelegate如下

    abstract class FlowDelegate {
      const FlowDelegate({ Listenable repaint }) : _repaint = repaint;
      final Listenable _repaint;
      //重写设置尺寸
      Size getSize(BoxConstraints constraints) => constraints.biggest;
      //重写设置约束
      BoxConstraints getConstraintsForChild(int i, BoxConstraints constraints) => constraints;
      //绘制children的位置和大小
      void paintChildren(FlowPaintingContext context);
      //是否要从新布局,可自己定制规则
      bool shouldRelayout(covariant FlowDelegate oldDelegate) => false;
      //是否从新绘制,可自己定制规则
      bool shouldRepaint(covariant FlowDelegate oldDelegate);
      @override
      String toString() => '$runtimeType';
    }
    
  • 构造函数

    Flow({Key key,
      @required this.delegate,//需要自定义的代理,来处理children的位置大小
      List<Widget> children = const <Widget>[],
    }) : assert(delegate != null),
         super(key: key, children: RepaintBoundary.wrapAll(children));
    
  • 使用

    Flow(
      delegate: TestFlowDelegate(margin: EdgeInsets.all(5.0)),
      children: <Widget>[
        new Container(
          width: 60.0,  height: 60.0, color: Colors.red,
          child: Text('红'), alignment: Alignment.center, 
        ),
        new Container(
          width: 60.0, height: 60.0,color: Colors.orange,
          child: Text('橙'),alignment: Alignment.center,  
        ),
        new Container(
          width: 60.0,height: 60.0,color: Colors.yellow,
          child: Text('黄'),alignment: Alignment.center,      
        ),
        new Container(
          width: 60.0,height: 60.0,color: Colors.green,
          child: Text('绿'),alignment: Alignment.center,  
        ),
        new Container(
          width: 60.0,height: 60.0,color: Colors.cyan,
          child: Text('青'),alignment: Alignment.center,     
        ),
        new Container(
          width: 60.0, height: 60.0,color: Colors.blue,
          child: Text('蓝'),alignment: Alignment.center,  
        ),
        new Container(
          width: 60.0,height: 60.0,color: Colors.purple,
          child: Text('紫'),alignment: Alignment.center,   
        ),
      ],
    )
        
    class TestFlowDelegate extends FlowDelegate {
      EdgeInsets margin = EdgeInsets.zero;
      TestFlowDelegate({this.margin});
    
      @override
      void paintChildren(FlowPaintingContext context) {
        var x = margin.left;
        var y = margin.top;
        for (int i = 0; i < context.childCount; i++) {
          var w = context.getChildSize(i).width + x + margin.right;
          if (w < context.size.width) {
            context.paintChild(i,
                transform: new Matrix4.translationValues(x, y, 0.0));
            x = w + margin.left;
          } else {
            x = margin.left;
            y += context.getChildSize(i).height + margin.top + margin.bottom;
            context.paintChild(i,
                transform: new Matrix4.translationValues(x, y, 0.0));
            x += context.getChildSize(i).width + margin.left + margin.right;
          }
        }
      }
    
      @override
      bool shouldRepaint(FlowDelegate oldDelegate) {
        return oldDelegate != this;
      }
    }
    
  • 效果如下
    Flutter中widget(二)--多子元素布局_第20张图片

九、Wrap也是流式布局

  • Flow可以很轻易的实现Wrap的效果,但是Wrap更多的是在使用了Flex中的一些概念,某种意义上说是跟Row、Column更加相似的。

  • 单行的Wrap跟Row表现几乎一致,单列的Wrap则跟Row表现几乎一致。但Row与Column都是单行单列的,Wrap则突破了这个限制,mainAxis上空间不足时,则向crossAxis上去扩展显示。

  • 从效率上讲,Flow肯定会比Wrap高,但是Wrap使用起来会方便一些。

  • 构造函数如下:

    Wrap({Key key,
      this.direction = Axis.horizontal,
      this.alignment = WrapAlignment.start,
      this.spacing = 0.0,
      this.runAlignment = WrapAlignment.start,
      this.runSpacing = 0.0,
      this.crossAxisAlignment = WrapCrossAlignment.start,
      this.textDirection,
      this.verticalDirection = VerticalDirection.down,
      List<Widget> children = const <Widget>[],
    }) : super(key: key, children: children);
    
    • direction :主轴方向,默认水平
    • alignment :主轴方向方式,值为WrapAlignment的枚举值,详情请看Row的对齐方式
    • spacing :主轴方向上child之间的间距,默认为0
    • runAlignment : 新一行或一列的对齐方式
    • runSpacing :新的一行或一列的间距,默认为0
    • crossAxisAlignment:交叉轴的对齐方式,默认是从主轴开始位置开始
    • textDirection:每一行或一列的排列方式
      • 如果一行有三个元素,则第一行取出前三个元素2,1,0这样排列
    • verticalDirection:垂直方向上排列方式,值为VerticalDirection的枚举值,默认从上到下
  • 使用

    Container(
        alignment: Alignment.topCenter,
        child:  Wrap(
        //主轴方向,默认水平
        direction: Axis.horizontal,
        //主轴方向方式,包裹在一个控件内效果明显,默认主轴方向开始位置开始
        alignment: WrapAlignment.spaceBetween,
        //主轴方向上child之间的间距,默认为0
        spacing: 6.0,
        // 新一行或一列的对齐方式
        runAlignment: WrapAlignment.spaceBetween,
        //新的一行或一列的间距,默认为0
        runSpacing: 0.0,
        //交叉轴的对齐方式,默认是从主轴开始位置开始
        crossAxisAlignment: WrapCrossAlignment.start,
        //每一行或一列的排列方式,如果一行有三个元素,则第一行取出前三个元素2,1,0这样排列
        //默认从左到右
        textDirection: TextDirection.ltr,
        //垂直方向上排列方式,默认从上到下
        verticalDirection: VerticalDirection.down,
        children: <Widget>[
          Chip(
            avatar: CircleAvatar(backgroundColor: Colors.blue.shade900, child: Text('1')),
            label: Text('Hamilton'),
          ),
          Chip(
            avatar: CircleAvatar(backgroundColor: Colors.blue.shade900, child: Text('2')),
            label: Text('Lafayette'),
          ),
          Chip(
            avatar: CircleAvatar(backgroundColor: Colors.blue.shade900, child: Text('3')),
            label: Text('Mulligan'),
          ),
          Chip(
            avatar: CircleAvatar(backgroundColor: Colors.blue.shade900, child: Text('4')),
            label: Text('Laurens'),
          ),
          Chip(
            avatar: CircleAvatar(backgroundColor: Colors.blue.shade900, child: Text('5')),
            label: Text('Hamilton'),
          ),
          Chip(
            avatar: CircleAvatar(backgroundColor: Colors.blue.shade900, child: Text('6')),
            label: Text('Lafayette'),
          ),
          Chip(
            avatar: CircleAvatar(backgroundColor: Colors.blue.shade900, child: Text('7')),
            label: Text('Mulligan'),
          ),
          Chip(
            avatar: CircleAvatar(backgroundColor: Colors.blue.shade900, child: Text('8')),
            label: Text('Laurens'),
          ),
          Chip(
            avatar: CircleAvatar(backgroundColor: Colors.blue.shade900, child: Text('9')),
            label: Text('Hamilton'),
          ),
          Chip(
            avatar: CircleAvatar(
                backgroundColor: Colors.blue.shade900, child: Text('10')),
            label: Text('Lafayette'),
          ),
          Chip(
            avatar: CircleAvatar(
                backgroundColor: Colors.blue.shade900, child: Text('11')),
            label: Text('Mulligan'),
          ),
          Chip(
            avatar: CircleAvatar(
                backgroundColor: Colors.blue.shade900, child: Text('12')),
            label: Text('Laurens'),
          ),
        ],
      ),
    )
    
  • 效果
    Flutter中widget(二)--多子元素布局_第21张图片

注:

  • 本例元素个数正好能排成矩阵型,这时设置的对齐方式保证两边对齐了,
  • 如果最后一行剩下两个元素,则此时的对齐方式将也是两端对齐,中间将空出来

你可能感兴趣的:(Flutter)