Flutter布局详解,必知必会
前⾔
本⽂的⽬的是为了让读者掌握不同布局类Widget的布局特点,分享⼀些在实际使⽤过程遇到的⼀些问题,在这本书中已经讲解的很详细了,本⽂主要是对其内容的浓缩及实际遇到的问题的补充。
什么是布局类Widget
布局类Widget就是指直接或间接继承(包含)MultiChildRenderObjectWidget的Widget,它们⼀般都会有⼀个children属性⽤于接收⼦Widget。在Flutter中Element树才是最终的绘制树,Element树是通过widget树来创建的(通过ateElement()),widget其实就是Element的配置数据。它的最终布局、UI界⾯渲染都是通过RenderObject对象来实现的,这⾥的细节我就不详细描述了,因为我也不懂。不过感兴趣的⼩伙伴也可以看看本专栏的这篇⽂章。
Flutter中主要有以下⼏种布局类的Widget:
线性布局Row和Column
弹性布局Flex
流式布局Wrap、Flow
层叠布局Stack、Positioned
线性布局Row和Column
线性布局其实是指沿⽔平或垂直⽅向排布⼦Widget,Flutter中通过Row来实现⽔平⽅向的⼦Widegt布局,通过Column来实现垂直⽅向的⼦Widget布局。他们都继承Flex,所以它们有很多相似的属性。
在前端的Flex布局中,默认存在两根轴:⽔平的主轴(main axis)和垂直的交叉轴(cross axis)。主轴的开始位置(与边框的交叉点)叫做main start,结束位置叫做main end;交叉轴的开始位置叫做cross start,结束位置叫做cross end。与Flutter中MainAxisAlignment和CrossAxisAlignment类似,分别代表主轴对齐和纵轴对齐。
源码属性解读
Row({
.....
MainAxisAlignment mainAxisAlignment = MainAxisAlignment.start,
MainAxisSize mainAxisSize = MainAxisSize.max,
CrossAxisAlignment crossAxisAlignment = ,
TextDirection textDirection,
VerticalDirection verticalDirection = VerticalDirection.down,
TextBaseline textBaseline,
List<Widget> children = const <Widget>[],flex布局对齐方式
})
Column({
.....
MainAxisAlignment mainAxisAlignment = MainAxisAlignment.start,
MainAxisSize mainAxisSize = MainAxisSize.max,
CrossAxisAlignment crossAxisAlignment = ,
TextDirection textDirection,
VerticalDirection verticalDirection = VerticalDirection.down,
TextBaseline textBaseline,
List<Widget> children = const <Widget>[],
})
复制代码
textDirection:表⽰⽔平⽅向⼦widget的布局顺序(是从左往右还是从右往左),默认为系统当前Locale环境的⽂本⽅向(如中⽂、英语
都是从左往右,⽽阿拉伯语是从右往左)。
主轴⽅向: Row即为⽔平⽅向,Column为垂直⽅向
mainAxisAlignment 主轴⽅向,对child起作⽤
center:将children放置在主轴的中⼼
start:将children放置在主轴的起点
end:将children放置在主轴的末尾
spaceAround:将主轴⽅向上的空⽩区域均分,使children之间的空⽩区域相等,但是⾸尾child的靠边间距为空⽩区域为1/2 spaceBetween:将主轴⽅向上的空⽩区域均分,使children之间的空⽩区域相等,⾸尾child靠边没有间隙
spaceEvenly:将主轴⽅向上的空⽩区域均分,使得children之间的空⽩区域相等,包括⾸尾child mainAxisSize max表⽰尽可能占多的控件,min会导致控件聚拢在⼀起
crossAxisAlignment 交叉轴⽅向,对child起作⽤
baseline:使children baseline对齐
center:children在交叉轴上居中展⽰
end:children在交叉轴上末尾展⽰
start:children在交叉轴上起点处展⽰
stretch:让children填满交叉轴⽅向
verticalDirection ,child的放置顺序
VerticalDirection.down,在Row中就是从左边到右边,Column代表从顶部到底部
VerticalDirection.up,相反
Row
⽰例代码
ListView(
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Text("我是Row的⼦控件  "),
Text("MainAxisAlignment.start")
],
),
Row(
mainAxisAlignment: ,
children: <Widget>[
Text("我是Row的⼦控件  "),
Text("")
],
)
,
Row(
mainAxisAlignment: d,
children: <Widget>[
Text("我是Row的⼦控件  "),
Text("d")
],
),
Row(
crossAxisAlignment: CrossAxisAlignment.start,
verticalDirection: VerticalDirection.up,
children: <Widget>[
Text(" Hello World ", style: TextStyle(fontSize: 30.0),),
Text(" I am Jack "),
],
],
)
复制代码
代码运⾏效果
前3个Row很简单,只是设置了主轴⽅向的对齐⽅式;第四个Row测试的是纵轴的对齐⽅式,由于两个⼦Text字体不⼀样,所以其⾼度也不同,我们指定了verticalDirection值为VerticalDirection.up,即从低向顶排列,⽽此时crossAxisAlignment值为CrossAxisAlignment.start表⽰底对齐。⼤家可以参考上⾯Row和Column的主侧轴的⽰意图,看看布局是不是正确的,还有很多种情况就不⼀⼀列举了。
Column
⽰例代码
ListView(children: <Widget>[
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text("我是Colum的⼦控件"),
Text("CrossAxisAlignment.start"),
],
),
Column(
crossAxisAlignment: ,
children: <Widget>[
Text("我是Colum的⼦控件"),
Text(""),
],
),
Column(
crossAxisAlignment: d,
children: <Widget>[
Text("我是Colum的⼦控件"),
Text("d"),
]
,
),
],)
复制代码
代码运⾏效果
Column和Row差不多,只是布局⽅向不⼀样⽽已,⼤家可以参考着看,这⾥就不再赘述了。
实际使⽤
由于篇幅有限,我就不详细讲解实际遇到的问题了,只说现象和解决办法:
如果Row⾥⾯嵌套Row,或者Column⾥⾯再嵌套Column,那么只有对最外⾯的Row或Column会占⽤尽可能⼤的空间,⾥⾯Row或Column所占⽤的空间为实际⼤⼩,如果要让⾥⾯的Colum或Row占满外部Colum或Row,可以使⽤Expanded widget
如果使⽤Column发现超范围,可⽤SingleChildScrollView包裹,scrollDirection属性设置滑动⽅向
使⽤Column嵌套ListView/GridView的时候,会报异常信息【Viewports expand in the scrolling direction to fill their
<】,这种情况flutter已给出解决办法,将ListView/GridView的 shrinkWrap属性设为true
有的时候修改Row/Column的verticalDirection会得到很好的效果,⽐如需要页⾯在底部需要⼏个按键,也可以⽤Stack来布局,但是相对⿇烦,⽽且有时还需要知道控件的⼤⼩,没有verticalDirection⽅便
弹性布局
弹性布局是⼀种允许⼦widget按照⼀定⽐例来分配⽗容器空间的布局⽅式,如果你知道了它的主轴⽅向,那就可以⽤Row或Column了,⼀般情况下,可以⽤Flex的地⽅都可以⽤Row或者Column⼀起使⽤,通常配合Expanded Widget来使⽤,同样Expanded也不能脱离Flex 单独创建。
Expanded
Expanded继承⾃Flexible,Flexible是⼀个控制Row、Column、Flex等⼦组件如何布局的组件,它可以按⽐例“扩伸”Row、Column和Flex⼦widget所占⽤的空间。
const Expanded({
int flex = 1,
@required Widget child,
})
复制代码
flex为弹性系数,如果为0或null,则child是没有弹性的,即不会被扩伸占⽤的空间。如果⼤于0,所有的Expanded按照其flex的⽐例来分割主轴的全部空闲空间。
⽰例代码
Row(children: <Widget>[
RaisedButton(
onPressed: () {
print('点击红⾊按钮事件');
},
color: d,
child: Text('红⾊按钮'),
),
Expanded(
flex: 1,
child: RaisedButton(
onPressed: () {
print('点击黄⾊按钮事件');
},
color: llow,
child: Text('黄⾊按钮'),
)
,
),
RaisedButton(
onPressed: () {
print('点击粉⾊按钮事件');
},
color: ,
child: Text('绿⾊按钮'),
),
])
复制代码
代码运⾏效果
Flexible和 Expanded的区别
Flexible组件必须是Row、Column、Flex等组件的后裔,并且从Flexible到它封装的Row、Column、Flex的路径必须只包括StatelessWidgets或StatefulWidgets组件(不能是其他类型的组件,像RenderObjectWidgets)
Row、Column、Flex会被Expanded撑开,充满主轴可⽤空间,⽽Flexible不强制⼦组件填充可⽤空间,这是因为fit属性的值不同,该属性在Expanded中为FlexFit.tight,Flexible为FlexFit.loose,区别在于tight表⽰强制使⼦控件填充剩余可⽤空间,loose表⽰最多填满其在⽗控件所设置的⽐例,所以loose默认即为控件的⼤⼩
流式布局
流式布局(Liquid)的特点(也叫"Fluid") 是页⾯元素的宽度按照屏幕分辨率进⾏适配调整,但整体布局不变。栅栏系统(⽹格系统),⽤户标签等。在Flutter中主要有Wrap和Flow两种Widget实现。
Wrap
在介绍Row和Colum时,如果⼦widget超出屏幕范围,则会报溢出错误,在Flutter中通过Wrap和Flow来⽀持流式布局,溢出部分则会⾃动折⾏。
源码属性解读

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。