以下内容为自学笔记,若有幸被大神看到,望指正其不准,补充其不足。万分感谢!!!
多子元素布局可以将多个子widget按一定的规则排列,实现项目中的需求。类似Android中的LinearLayout
、RelativeLayout
、FrameLayout
等布局。
拥有多子元素的布局总体有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 | 使用一个委托来对多个孩子进行设置大小和定位的小部件 |
LinearLayout
。RelativeLayout
。Flex
,而Flex
的父类为:MultiChildRenderObjectWidget
MainAxisAlignment.start
,取值为MainAxisAlignment
的枚举值。Row
:主轴方向为水平X轴。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'),
],
),
spaceAround
:沿主轴方向,将空白区域均分,使得子widget之间的空白区域相等,但首尾子widget临近边框的空白区域为1/2;
new Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[...],
),
spaceEvenly
:沿主轴方向,将空白区域(子widget的数量+1)等分,使得子widget之间的空白区域相等,首尾也留白;
new Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[...],
),
MainAxisSize.max
,取值范围为MainAxisSize
的枚举值。MainAxisSize.max
:最大化的占用主轴空间,最大约束值为父类的最大宽或高,但Row
的最大不会无限增大,如果Row
长度超过父类的宽(高)或超过屏幕边缘则会触发断言。MainAxisSize.min
:根据子widget的长度适配占用主轴空间,但Row
的长度不会无限增大,如果Row
长度超过父类的宽(高)或超过屏幕边缘则会触发断言。MainAxisSize.min
时;mainAxisAlignment
中只有start
生效。使用方式:
new Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
mainAxisSize: MainAxisSize.man,
children: <Widget>[...],
),
CrossAxisAlignment.center
,取值为CrossAxisAlignment
的枚举值。Row
的交叉轴:为垂直轴(Y轴);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>[...],),
),
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>[同上],
),
),
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>[同上],
),
),
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>[同上],
),
),
baseline
:子widget之间,在交叉轴方向上,是它们的基线适配
textBaseline
属性配合使用,否则会报错,示例看textBaseline
内;Column
中,作用相当于start
,因为基线总是水平的;描述文本内容的基线类型,取值为TextBaseline
的枚举值;
此属性在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>[...],
),
定义了子widget的在垂直方向上的排列顺序,并影响垂直方向的对齐方式,默认值为VerticalDirection.down
;
取值为VerticalDirection
内的枚举值,如下:
up
:子widget从bottom开始往top排列;down
:子widget从top开始往bottom排列。由于只作用在垂直方向,所以在Row
和Column
中作用效果不同:
Row
:对应crossAxisAlignment
的start
和end
属性,由于子widget为水平排列,所以不影响排列顺序,但会影响垂直方向的对齐方式;Column
:对应mainAxisAlignment
的start
和end
属性,影响子widget元素的排列顺序,并且会影响垂直方向的对齐方式。使用方式:
new Row(
crossAxisAlignment: CrossAxisAlignment.start,
verticalDirection: VerticalDirection.up,
children: <Widget>[...],
),
//---------------Column----------------------------
new Column(
mainAxisAlignment: MainAxisAlignment.end,
verticalDirection: VerticalDirection.down,
children: <Widget>[...],
),
verticalDirection
类似,只是定义的子widget在水平方向上的排列顺序,并影响水平方向的对齐方式;TextDirection
的枚举值包含:
ltr
:left to right,从左到右排列;rtl
:rigth to left,从右到左排列;Row
和Column
中作用效果不同:
Row
:对应mainAxisAlignment
的start
和end
属性,影响影响子widget元素排列顺序,并影响水平方向的对齐方式;Column
:对应crossAxisAlignment
的start
和end
属性,由于子widget为垂直排列,所以不影响排列顺序,但会影响水平方向的对齐方式。使用方式:
new Row(
mainAxisAlignment: CrossAxisAlignment.start,
textDirection: TextDirection.rtl,
children: <Widget>[...],
),
//---------------Column----------------------------
new Column(
crossAxisAlignment: MainAxisAlignment.end,
textDirection: TextDirection.rtl,
children: <Widget>[...],
),
是一个Widget
类型的list
集合,是填充布局的元素。
Stack
:叠加布局,即在Z轴方向上叠加子widget列表。类似Android中的FrameLayout
;Stack
的父类为:MultiChildRenderObjectWidget
IndexedStack
:是Stack
的子类,可指定显示子widget列表中的一个子widget。IndexedStack
中多一个index
属性指定显示子widget列表中的哪个widget。Positioned
来对子widget元素来定位,建议在三个以上子widget元素时使用;辅助Stack内子widget元素定位。有以下属性:
width
:子widget元素的宽。left
:子元素距离左边的距离。right
:子元素距右边的距离。height
:子widget元素的高。top
:子元素距离顶部的距离。bottom
:子元素距离底部的距离。注:
width
,left
和right
三个属性不能同时使用,必须至少有一个属性是null;height
,top
和bottom
三个属性不能同时使用,必须至少有一个属性是null。
叠加对齐方式,默认是AlignmentDirectional.topStart
左上对齐叠加;
详情请移步单子元素布局中Container的alignment
AlignmentDirectional
:
textDirction
配合使用改变对齐方式。topStart
、topCenter
、topEnd
、centerStart
、center
、centerEnd
、bottomStart
、bottomCenter
、bottomEnd
,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,),
],
),
AlignmentDirectional.xxxStart(或xxxend)
配合使用可以改变对齐方式。TextDirction
中的枚举值ltr
和rtl
,使用方式和效果与Row
类似。调整子widget集合中未定位元素(可使用辅助组件Positioned
定位)的大小;
默认值为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,),),
],
),
),
Overflow.clip
;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,),),
],
),
),
是一个Widget
类型的list
集合,是填充布局的元素。
StackFit.loose
,构造函数传递给父类中fit
属性。loose
是效果。indexedStack
特有属性,用来指定显示那个子widget元素;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,),),
],
),
ListVIew
和RecycleView
;GridView
相似,都是继承自BoxScrollView
,区别是GridView
是二维的,类似网格状。ListView
是在主轴方向可以滚动,在交叉轴方向,则是填满。List()
:默认构造;ListView.builder()
;ListView.separated
;ListView.custom
。序号 | 字段 | 类型 | 作用 |
---|---|---|---|
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 | builder 和separated 构造函数特有要传入参数,定义item的样式 |
13 | separatorBuilder | IndexedWidgetBuilder | separated 构造函数特有要传入的参数,定义分割线的样式 |
14 | itemCount | int | 列表可滚动的item数量,默认构造没有 |
15 | childrenDelegate | SliverChildDelegate | 内部都是靠此类实现滚动和布局,但只有custom 构造函数需要传入,其余都内部实现 |
默认构造,使用与内容较少的情形,因为它是把数据直接放到列表,一次性渲染所以内容;当item数目过多时,容易出现卡顿现象,导致滑动不畅;
如果需要设置分割线,则需要对列表item添加处理,在item之间(在奇数位)加一个分割线widget:Divider()
。
在children
属性中直接布局。
参数介绍
scrollDirection
:主轴滚动方向
Axis.vertical
;Axis
的枚举值:垂直vertical,竖直horizontal 。reverse
:是否反向,controller
:滚动控制器,默认为nullprimary
:是否强制滚动(到达顶部或底部时是否可以滚动)
controller
必须为nullphysics
:视图如何响应用户的手势滑动 ,可忽略primary
属性
const AlwaysScrollableScrollPhysics();
const ScrollPhysics();
shrinkWrap
:滚动视图在[滚动方向]中的范围是否应由正在查看的内容决定
itemExtent
:item交叉轴方向的大小
addAutomaticKeepAlives
:是否自动保存滑出屏幕外的字widget的状态
addRepaintBoundaries
: 是否放置到重绘列表中
cacheExtent
:缓存区大小,默认为250。children
: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'),
],
),
itemBuilder
和itemCount
替换了默认构造函数中的children
。itemBuilder
为必写属性,此属性定义item的样式。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]);
},
),
这是一个自带分割线的item,与builder构造类似,又多了一个参数separatorBuilder
用于控制列表各个元素的间隔如何渲染。
此构造函数中itemBuilder
、separatorBuilder
和itemCount
三个参数为必写参数。
内部实现类似上个代码中分割线处理
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,
),
childrenDelegate
然后传入一个实现了SliverChildDelegate
的组件,如下,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,
),
),
一个二维的可滚动的widget的数组布局。类似Android的GridView
效果。
参数和ListView
一样,唯一的区别是多了交叉轴方向的参数gridDelegate
,来控制交叉轴。
提供了5个构造函数,使用和ListView
一样:
GridView()
:GridVIew.builder()
:GridView.costom
:GirdView.count()
:GridView.extent()
。提供了5个构造函数,使用和ListView
一样:
GridView()
:
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()
:
itemBuilder
和gridDelegate
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
:
childrenDelegate
和gridDelegate
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()
:
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'),),
],
),
此参数为控制交叉轴方向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交叉轴与主轴方向长度的比例
Row
、Column
、ListView
、Flex
等。mainAxis
:排列的主轴方向reverse
:是否反向children
:子widget的集合Row
和Column
类似,通常很少使用吃控件。MultiChildRenderObjectWidget
ListBody({Key key,
//排列的主轴方向,默认为垂直,
//不可为空,并且与父布局主轴方向一致
this.mainAxis = Axis.vertical,
this.reverse = false,//是否反向
List<Widget> children = const <Widget>[],
}) : assert(mainAxis != null),
super(key: key, children: children);
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),
],
)
],
)
RenderObjectWidget
columnWidths
属性单独控制。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 //每行的内容
});
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'),
]),
],
),
)
根据自定义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;
}
}
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之间的间距,默认为0runAlignment
: 新一行或一列的对齐方式runSpacing
:新的一行或一列的间距,默认为0crossAxisAlignment
:交叉轴的对齐方式,默认是从主轴开始位置开始textDirection
:每一行或一列的排列方式
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'),
),
],
),
)
注:
- 本例元素个数正好能排成矩阵型,这时设置的对齐方式保证两边对齐了,
- 如果最后一行剩下两个元素,则此时的对齐方式将也是两端对齐,中间将空出来