Flutter
中拥有30多种预约义的布局widget
,经常使用的有Container
、Padding
、Center
、Flex
、Row
、Colum
、ListView
、GridView
。按照《Flutter技术入门与实战》上面来讲的话,大概分为四类git
二,宽高尺寸处理github
SizedBox布局行为相对较简单:less
Diagnosticable > DiagnosticableTree > Widget > RenderObjectWidget > SingleChildRenderObjectWidget > SizedBox
const SizedBox({
Key key,
this.width, //宽
this.height, //高
Widget child //子组件
})
width:宽度值,若是具体设置了,则强制child宽度为此值,若是没设置,则根据child宽度调整自身宽度。ide
height:同上。函数
这个控件的做用是添加额外的限制条件(constraints)到child上,自己挺简单的,能够被一些控件替换使用。Flutter的布局控件体系,梳理着发现确实有点乱,感受整体思想是缺啥就造啥
ConstrainedBox的布局行为很是简单,取决于设置的限制条件,而关于父子节点的限制因素生效优先级,能够查看以前的文章,在这里就不作具体叙述了。
Object > Diagnosticable > DiagnosticableTree > Widget > RenderObjectWidget > SingleChildRenderObjectWidget > ConstrainedBox
ConstrainedBox({ Key key, @required this.constraints, Widget child })
LimitedBox布局
Object > Diagnosticable > DiagnosticableTree > Widget > RenderObjectWidget > SingleChildRenderObjectWidget > LimitedBox
const LimitedBox({ Key key, this.maxWidth = double.infinity, this.maxHeight = double.infinity, Widget child, })
maxWidth:限定的最大宽度,默认值是double.infinity,不能为负数。post
maxHeight:同上。性能
AspectRatio的布局行为分为两种状况:学习
Object > Diagnosticable > DiagnosticableTree > Widget > RenderObjectWidget > SingleChildRenderObjectWidget > AspectRatio
从继承关系看,AspectRatio是基础的布局控件。ui
const AspectRatio({ Key key, @required this.aspectRatio, Widget child })
构造函数只包含了一个aspectRatio属性,其中aspectRatio不能为null。
FractionallySizedBox的布局行为主要跟它的宽高因子两个参数有关,当参数为null或者有具体数值的时候,布局表现不同。固然,还有一个辅助参数alignment,做为对齐方式进行布局。
Object > Diagnosticable > DiagnosticableTree > Widget > RenderObjectWidget > SingleChildRenderObjectWidget > FractionallySizedBox
const FractionallySizedBox({ Key key, this.alignment = Alignment.center, this.widthFactor, this.heightFactor, Widget child, })
alignment:对齐方式,不能为null。
widthFactor:宽度因子,跟以前介绍的控件相似,宽度乘以这个值,就是最后的宽度。
heightFactor:高度因子,用做计算最后实际高度的。
其中widthFactor和heightFactor都有一个规则
三,经常使用方法
/** * MySizeBox */ class MySizeBox extends StatelessWidget { @override Widget build(BuildContext context) { // TODO: implement build return new Container( color: Colors.green, padding: const EdgeInsets.all(5.0), child: SizedBox( width: 200.0, height: 200.0, child: new Container( color: Colors.red, width: 100.0, height: 300.0, ), ), ); } }
效果图:
源码解析:
SizedBox内部是经过RenderConstrainedBox来实现的。具体的源码就不解析了,整体思路是,根据宽高值算好一个constraints,而后强制应用到child上。
/** * ConstrainedBox * 在一个宽高200.0的Container上添加一个约束最大最小宽高的ConstrainedBox,实际的显示中,则是一个宽高为150.0的区域。 */ class MyConstrainedBox extends StatelessWidget { @override Widget build(BuildContext context) { // TODO: implement build return new ConstrainedBox( constraints: const BoxConstraints( minWidth: 100.0, minHeight: 100.0, maxWidth: 150.0, maxHeight: 150.0, ), child: new Container( width: 200.0, height: 200.0, color: Colors.red, ), ); } }
效果图:
@override RenderConstrainedBox createRenderObject(BuildContext context) { return new RenderConstrainedBox(additionalConstraints: constraints); }
RenderConstrainedBox实现其绘制。其具体的布局表现分两种状况:
若是child不为null,则将限制条件加在child上;
若是child为null,则会尽量的缩小尺寸。
具体代码以下:
@override void performLayout() { if (child != null) { child.layout(_additionalConstraints.enforce(constraints), parentUsesSize: true); size = child.size; } else { size = _additionalConstraints.enforce(constraints).constrain(Size.zero); } }
使用场景:
须要添加额外的约束条件可使用此控件,例如设置最小宽高,尽量的占用区域等等。笔者在实际开发中使用的倒不是不少,倒不是说这个控件很差使用,而是好多约束因素是综合的,例如须要额外的设置margin、padding属性能,所以单独再套个这个就显得很繁琐了。
class MyLimitedBox extends StatelessWidget { @override Widget build(BuildContext context) { // TODO: implement build return new Row( children: <Widget>[ new Container( color: Colors.red, width: 100.0, ), LimitedBox( maxWidth: 150.0, child: new Container( color: Colors.blue, width: 250.0, ), ) ], ); } }
效果图:
源码解析:
先不说其源码,单纯从其做用,前面介绍的SizedBox、ConstrainedBox都相似,都是经过强加到child的constraint,来达到相应的效果。
咱们直接看其计算constraint的代码
minWidth: constraints.minWidth, maxWidth: constraints.hasBoundedWidth ? constraints.maxWidth : constraints.constrainWidth(maxWidth), minHeight: constraints.minHeight, maxHeight: constraints.hasBoundedHeight ? constraints.maxHeight : constraints.constrainHeight(maxHeight)
LimitedBox只是改变最大宽高的限定。具体的布局代码以下:
if (child != null) { child.layout(_limitConstraints(constraints), parentUsesSize: true); size = constraints.constrain(child.size); } else { size = _limitConstraints(constraints).constrain(Size.zero); }
根据最大尺寸,限制child的布局,而后将自身调节到child的尺寸。
使用场景:
是不可能清楚了,光是找例子,就花了很多时间。Flutter的一些冷门控件,真的是除了官方的文档,啥材料都木有。
谷歌说这个颇有用,仍是一脸懵逼。这种控件,也有其余的替代解决方案,LimitedBox能够达到的效果,ConstrainedBox均可以实现。
/** * AspectRatio * 定义了一个高度为200的区域,内部AspectRatio比率设置为1.5,最终AspectRatio的是宽300高200的一个区域。 */ class MyAspectRatio extends StatelessWidget { @override Widget build(BuildContext context) { // TODO: implement build return new Container( height: 200.0, child: new AspectRatio( aspectRatio: 1.5, child: new Container( color: Colors.red, ), ), ); } }
效果图:
@override RenderAspectRatio createRenderObject(BuildContext context) => new RenderAspectRatio(aspectRatio: aspectRatio);
通过前面一些控件的解析,我想你们对这种构造应该不会再陌生了,绘制都是交由RenderObject去完成的,这里则是由RenderAspectRatio去完成具体的绘制工做。
RenderAspectRatio的构造函数中会对aspectRatio作一些检测(assert)
aspectRatio不能为null;
aspectRatio必须大于0;
aspectRatio必须是有限的。
接下来咱们来看一下RenderAspectRatio的具体尺寸计算函数:
(1)若是限制条件为isTight,则返回最小的尺寸(constraints.smallest);
if (constraints.isTight) return constraints.smallest;
(2)若是宽度为有限的值,则将高度设置为width / _aspectRatio。 若是宽度为无限,则将高度设为最大高度,宽度设为height * _aspectRatio;
if (width.isFinite) { height = width / _aspectRatio; } else { height = constraints.maxHeight; width = height * _aspectRatio; }
(3)接下来则是在限制范围内调整宽高,整体思想则是宽度优先,大于最大值则设为最大值,小于最小值,则设为最小值。
若是宽度大于最大宽度,则将其设为最大宽度,高度设为width / _aspectRatio;
if (width > constraints.maxWidth) { width = constraints.maxWidth; height = width / _aspectRatio; }
若是高度大于最大高度,则将其设为最大高度,宽度设为height * _aspectRatio;
if (height > constraints.maxHeight) { height = constraints.maxHeight; width = height * _aspectRatio; }
若是宽度小于最小宽度,则将其设为最小宽度,高度设为width / _aspectRatio;
if (width < constraints.minWidth) { width = constraints.minWidth; height = width / _aspectRatio; }
若是高度小于最小高度,则将其设为最小高度,宽度设为height * _aspectRatio。
if (height < constraints.minHeight) { height = constraints.minHeight; width = height * _aspectRatio; }
/** * MyFractionallySizeBox */ class MyFractionallySizeBox extends StatelessWidget { @override Widget build(BuildContext context) { // TODO: implement build return new Container( color: Colors.blue, height: 150.0, width: 150.0, padding: const EdgeInsets.all(10.0), child: new FractionallySizedBox( alignment: Alignment.topLeft, widthFactor: 1.5, heightFactor: 0.5, child: new Container( color: Colors.red, ), ), ); } }
效果图:
源码解析:
FractionallySizedBox内部具体渲染是由RenderFractionallySizedOverflowBox来实现的,经过命名就能够看出,这个控件可能会Overflow。
咱们直接看实际计算尺寸的代码
double minWidth = constraints.minWidth; double maxWidth = constraints.maxWidth;
if (_widthFactor != null) { final double width = maxWidth * _widthFactor; minWidth = width; maxWidth = width; }
double minHeight = constraints.minHeight; double maxHeight = constraints.maxHeight;
if (_heightFactor != null) { final double height = maxHeight * _heightFactor; minHeight = height; maxHeight = height; }
源代码中,根据宽高因子是否存在,来进行相对应的尺寸计算。
使用场景:
当须要在一个区域里面取百分比尺寸的时候,可使用这个,比方说,高度40%宽度70%的区域。固然,AspectRatio也能够达到近似的效果。