本文主要介绍Flutter布局中的SizedOverflowBox、Transform、CustomSingleChildLayout三种控件,详细介绍了其布局行为以及使用场景,并对源码进行了分析。html
A widget that is a specific size but passes its original constraints through to its child, which will probably overflow.git
光看名称,就能够猜出,SizedOverflowBox是SizedBox与OverflowBox的结合体。github
SizedOverflowBox主要的布局行为有两点:性能优化
Object > Diagnosticable > DiagnosticableTree > Widget > RenderObjectWidget > SingleChildRenderObjectWidget > SizedOverflowBox
Container( color: Colors.green, alignment: Alignment.topRight, width: 200.0, height: 200.0, padding: EdgeInsets.all(5.0), child: SizedOverflowBox( size: Size(100.0, 200.0), child: Container(color: Colors.red, width: 200.0, height: 100.0,), ), );
代码运行的时候报出了下面的异常,很神奇。可是同窗们能够本身看看代码运行的效果,能够超出,可是不太好用。app
══╡ EXCEPTION CAUGHT BY RENDERING LIBRARY ╞═════════════════════════════════════════════════════════
const SizedOverflowBox({ Key key, @required this.size, this.alignment = Alignment.center, Widget child, })
size:固定的尺寸。ide
alignment:对齐方式。函数
直接上布局相关的代码:布局
size = constraints.constrain(_requestedSize); if (child != null) { child.layout(constraints); alignChild(); }
若是child存在的话,就将child设为对应的尺寸,而后按照对齐方式进行对齐。可是在实际写sample的时候,感受跟我预想中的表现不太一致,并且常常报出异常。不知道是我理解错了,仍是样例写错了,若是有了解的同窗,麻烦告知,在此感谢。性能
代码的表现跟我预想中的不太一致,更推荐使用OverflowBox,场景也跟其比较一致。学习
A widget that applies a transformation before painting its child.
Transform在介绍Container的时候有提到过,就是作矩阵变换的。Container中矩阵变换就是使用的Transform。
有过其余平台经验的,对Transform应该不会陌生。能够对child作平移、旋转、缩放等操做。
Object > Diagnosticable > DiagnosticableTree > Widget > RenderObjectWidget > SingleChildRenderObjectWidget > Transform
Center( child: Transform( transform: Matrix4.rotationZ(0.3), child: Container( color: Colors.blue, width: 100.0, height: 100.0, ), ), )
示例中将Container绕z轴旋转了,代码很简单。Matrix4也提供了不少便捷的构造函数供你们使用,所以写起来,并不会有太大的难度。
const Transform({ Key key, @required this.transform, this.origin, this.alignment, this.transformHitTests = true, Widget child, })
上面是其默认的构造函数,Transform也提供下面三种构造函数:
Transform.rotate Transform.translate Transform.scale
transform:一个4x4的矩阵。不难发现,其余平台的变换矩阵也都是四阶的。一些复合操做,仅靠三维是不够的,必须采用额外的一维来补充,感兴趣的同窗能够自行搜索了解。
origin:旋转点,相对于左上角顶点的偏移。默认旋转点事左上角顶点。
alignment:对齐方式。
transformHitTests:点击区域是否也作相应的改变。
咱们来看看它的绘制代码:
if (child != null) { final Matrix4 transform = _effectiveTransform; final Offset childOffset = MatrixUtils.getAsTranslation(transform); if (childOffset == null) context.pushTransform(needsCompositing, offset, transform, super.paint); else super.paint(context, offset + childOffset); }
整个绘制代码不复杂,若是child有偏移的话,则将两个偏移相加,进行绘制。若是child没有偏移的话,则按照设置的offset、transform进行绘制。
这个控件算是较常见的控件,不少平移、旋转、缩放均可以使用的到。若是只是单纯的进行变换的话,用Transform比用Container效率会更高。
A widget that defers the layout of its single child to a delegate.
一个经过外部传入的布局行为,来进行布局的控件,不一样于其余固定布局的控件,咱们自定义一些单节点布局控件的时候,能够考虑使用它。
CustomSingleChildLayout提供了一个控制child布局的delegate,这个delegate能够控制这些因素:
Object > Diagnosticable > DiagnosticableTree > Widget > RenderObjectWidget > SingleChildRenderObjectWidget > CustomSingleChildLayout
class FixedSizeLayoutDelegate extends SingleChildLayoutDelegate { FixedSizeLayoutDelegate(this.size); final Size size; @override Size getSize(BoxConstraints constraints) => size; @override BoxConstraints getConstraintsForChild(BoxConstraints constraints) { return new BoxConstraints.tight(size); } @override bool shouldRelayout(FixedSizeLayoutDelegate oldDelegate) { return size != oldDelegate.size; } } Container( color: Colors.blue, padding: const EdgeInsets.all(5.0), child: CustomSingleChildLayout( delegate: FixedSizeLayoutDelegate(Size(200.0, 200.0)), child: Container( color: Colors.red, width: 100.0, height: 300.0, ), ), )
因为SingleChildLayoutDelegate是一个抽象类,咱们须要单独写一个继承自它的delegate,来进行相关的布局操做。上面例子中是一个固定尺寸的布局。
构造函数以下:
const CustomSingleChildLayout({ Key key, @required this.delegate, Widget child })
alignment:一个抽象类,咱们须要自行实现布局的类。
咱们直接来看其布局函数:
size = _getSize(constraints); if (child != null) { final BoxConstraints childConstraints = delegate.getConstraintsForChild(constraints); assert(childConstraints.debugAssertIsValid(isAppliedConstraint: true)); child.layout(childConstraints, parentUsesSize: !childConstraints.isTight); final BoxParentData childParentData = child.parentData; childParentData.offset = delegate.getPositionForChild(size, childConstraints.isTight ? childConstraints.smallest : child.size); }
其child的constraints是经过delegate传入的,而这个delegate则是咱们经过外部继承自SingleChildLayoutDelegate实现的。
咱们接下来看一下SingleChildLayoutDelegate提供了哪些接口:
Size getSize(BoxConstraints constraints) => constraints.biggest; BoxConstraints getConstraintsForChild(BoxConstraints constraints) => constraints; Offset getPositionForChild(Size size, Size childSize) => Offset.zero; bool shouldRelayout(covariant SingleChildLayoutDelegate oldDelegate);
其中前三个都是布局相关的,包括尺寸、constraints、位置。最后一个函数是判断是否须要从新布局的。咱们在编写delegate的时候,能够选择须要进行修改的属性进行重写,并给予相应的布局属性便可。
这种控件用起来可能会繁琐一些,可是咱们能够经过这个控件去封装一些基础的控件,供他人使用。
到目前为止,Flutter中单子节点布局控件,大体上都简单的梳理了一遍。若是一直在关注这个系列文章的同窗,应该能够发现我常常吐槽其控件设计。
Flutter中总共有18个单子节点布局控件,18个啊,尚未算多子节点布局控件,也没有算可能会新增的。这样的学习成本很是高。虽然常见的就那么几种,平时一直用那些也都没有问题,可是布局的时候老是会遇到那么几种解决不了的问题。并且梳理的时候,会发现,几种控件都能解决的问题,并非说把效果实现出了就完事了,这中间确定会涉及到哪一种控件效率更高。若是要对Flutter项目作较深刻的性能优化,这些控件确定都得掌握。
Flutter的这种设计,把一些本来应该由它们承担的成本,转移到了开发者身上。不少控件在平常使用中几乎都用不上,并无考虑太多实际的使用场景。固然了,也仍是得安慰本身,这毕竟只是初期,乱点就乱点,日子确定会愈来愈好的。
后面还会将多子节点控件所有梳理一遍,而后来个大总结,对什么场景该使用哪一种控件,如何进行控件级别的优化,作一个总结。
笔者建了一个Flutter学习相关的项目,Github地址,里面包含了笔者写的关于Flutter学习相关的一些文章,会按期更新,也会上传一些学习Demo,欢迎你们关注。