【Flutter 组件集录】ConstrainedBox | 8 月更文挑战

前言:

这是我参与8月更文挑战的第 22 天,活动详情查看:8月更文挑战。为应掘金的八月更文挑战,我准备在本月挑选 31 个以前没有介绍过的组件,进行全面分析和属性介绍。这些文章将来会作为 Flutter 组件集录 的重要素材。希望可以坚持下去,你的支持将是我最大的动力~

| 本系列 | 组件文章 | 列表 | | ------------------------------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ | | 1.NotificationListener | 2.Dismissible | 3.Switch | | 4.Scrollbar | 5.ClipPath | 6.CupertinoActivityIndicator | | 7.Opacity | 8.FadeTransition | 9. AnimatedOpacity | | 10. FadeInImage | 11. Offstage | 12. TickerMode | | 13. Visibility | 14. Padding | 15. AnimatedContainer | | 16.CircleAvatar | 17.PhysicalShape | 18.Divider | | 19.Flexible、Expanded 和 Spacer | 20.Card | 21.SizedBox | | 22.ConstrainedBox [本文] | | |


一、 认识 ConstrainedBox 组件

源码中对 ConstrainedBox 的介绍为:为子组件施加额外的约束。那什么是约束?为什么说是 额外的 ?约束的作用是什么? 通过本文一起来看一下。


1.ConstrainedBox 基本信息

下面是 ConstrainedBox 组件类的定义构造方法,可以看出它继承自 SingleChildRenderObjectWidget。可接受一个子组件,在构造时必须传入 constraints 参数,其类型为 BoxConstraints

dart /// The additional constraints to impose on the child. final BoxConstraints constraints;


2.认识约束 BoxConstraints

BoxConstraints 类是对区域范围的抽象,维护着四个值: 这四个值组成了一个尺寸的取值区域,来限定子组件的尺寸大小。这里再强调一下,组件本身是没有尺寸概念的,这里说的组件尺寸,是指其维护的渲染对象尺寸。

| 成员对象 | 对象类型 | 默认值 | 介绍 | | --------- | -------- | --------------- | -------------- | | minWidth | double | 0 | 尺寸宽度最小值 | | maxWidth | double | double.infinity | 尺寸宽度最大值 | | minHeight | double | 0 | 尺寸高度最小值 | | maxHeight | double | double.infinity | 尺寸高度最大值 |


BoxConstraints 就是 RenderObject 的一个属性,负责自身区域约束,并结合额外约束向下层传递。也就是说,每一个 RenderObject 都具有约束属性。比如下面是 100*50SizedBox ,使用 ColoredBox 涂上蓝色。

dart SizedBox( width: 100, height: 50, child: ColoredBox( color: Colors.blue.withAlpha(88) ), ),

可以看出 SizedBox 组件维护着 RenderConstrainedBox 渲染对象,其自身的约束是上层施加的 [w(0,800) - h(0,600)] ,也就是说该渲染对象的尺寸需要在这个范围内。除此之外,它会给子节点施加额外的 [w(100,100) - h(50,50)] 约束。这样约束传递给 ColoredBox 对应的渲染对象 _RenderColoredBox ,其约束为 [w(100,100) - h(50,50)] ,就决定了 _RenderColoredBox 尺寸被限定为 (100,50)

这样应该对约束是什么有了一个简单的认识。


3.ConstrainedBox 的使用

下面先来通过一个简单的例子看一下 ConstrainedBox 的作用 。在上面案例的基础上,限定宽度在 [10~40] 之内。可以看出,即使子组件使用 SizedBox 明确表示自己想要 100*50 的尺寸,但由于这里 ConstrainedBox 施加的约束,SizedBox的宽度也必须在 1040 之间。

dart ConstrainedBox( constraints: BoxConstraints( minWidth: 10, maxWidth: 40, ), child: SizedBox( width: 100, height: 50, child: ColoredBox( color: Colors.blue.withAlpha(88) ), ), );

如下所示,SizedBox 对应的渲染对象,本身约束为 [w(10,40) - h(0,600)] ,向子节点额外的约束为 [w(100,100) - h(50,50)] ,这两个约束会结合起来,成为对下一个子节点的约束。


二、进一步认识 BoxConstraints

简单来说 BoxConstraints 就是维护着四个值,但通过这四个值衍生出来的对象还是值得梳理一下的。

1. BoxConstraints 的构造方法

BoxConstraints 一共有 6 种构造方法,普通构造传入四个值,之前看过了。


.tight 构造 ,需要传入一个 Size 对象,将约束区域为指定宽高 , 和 SizedBox 作用是一致的。


.tightFor 构造, 传入的宽高值。和 tight 不同的是这里宽/高可谓空,如果宽为空则宽度取值范围在 0~无限 之间,高度也类似。在上一篇 SizedBox 一文中我们说过,SizedBox 的内部就是使用 .tightFor 根据宽高构造约束对象的。也就是说,你使用 tightFor 创建约束,用于 ConstrainedBox 中,本质上和 SizedBox 一样。


.expand 构造,传入宽和高。和 tightFor 类似,宽/高可空。不同点在于:如果宽为空,则宽度取值范围在 无限~无限 之间,也就是说区域无限,会尽可能扩充。


.loose 构造,和.tight 类似 ,需要传入一个 Size 对象。可以看出下界是 0 ,上界是 size 的宽高。所以这种的约束是松散的,并不像 .tight 会将宽高定死。


.tightForFinite 构造,默认宽高无限。拿宽举例,默认情况下 width无限,取值区间为 0~无限,如果传入的 width 非无限,那么宽度将被固定为 width ,高度也类似。


2.BoxConstraints 的成员方法

BoxConstraints 的成员方法很多,但也都是围绕这四个属性值进行操作的,这里挑几个重要的方法看看。首先来看 constrainWidth 。可以看出需要传入一个 width 参数,默认是无限,返回值使用了 num.clamp 函数。

dart double constrainWidth([ double width = double.infinity ]) { assert(debugAssertIsValid()); return width.clamp(minWidth, maxWidth); }

这个函数可能大家很少用,下面通过一个方法测试一下。可以看出 clamp 的参数是上下界:如果小于下界,则返回下界值,如果大于上界,则返回上界值;如果在上下界直接,则返回该值。也就是说,一个返回值不会超过 clamp 参数的上下界。这个方法非常契合约束的理念。

dart main(){ int a = 10.clamp(3, 6); // 6 int b = 4.clamp(3, 6); // 4 int c = 1.clamp(3, 6); // 3 print('$a,$b,$c'); }

同样 constrainHeight 也是类似的。

dart double constrainHeight([ double height = double.infinity ]) { assert(debugAssertIsValid()); return height.clamp(minHeight, maxHeight); }


enforce 方法算是一个非常重要的方法,它可以整合两个约束,生成一个新约束。可以看出,会以入参constraints 为范围区间,本约束的四个值通过 clamp 进行计算,得到新值。这样就保证,以 本约束 为优先,以 入参约束 为兜底。 可以结合上面的案例思考一下这种约束叠加的方式。

dart BoxConstraints enforce(BoxConstraints constraints) { return BoxConstraints( minWidth: minWidth.clamp(constraints.minWidth, constraints.maxWidth), maxWidth: maxWidth.clamp(constraints.minWidth, constraints.maxWidth), minHeight: minHeight.clamp(constraints.minHeight, constraints.maxHeight), maxHeight: maxHeight.clamp(constraints.minHeight, constraints.maxHeight), ); }


constrain 方法,入参是一个 Size 对象,且返回一个 Size 对象。也就是说,通过该 约束和一个尺寸返回新 尺寸。从下面的代码中也看出,就是新尺寸就是通过 constrainWidthconstrainHeight 对原尺寸进行运算而已。也就是说新尺寸是在入参尺寸的基础上,宽高尽量符合入参尺寸

dart Size constrain(Size size) { Size result = Size(constrainWidth(size.width), constrainHeight(size.height)); assert(() { result = _debugPropagateDebugSize(size, result); return true; }()); return result; }


三、ConstrainedBox 源码解读

ConstrainedBox 继承自 SingleChildRenderObjectWidget ,需要维护一个渲染对象。


实现约束功能的渲染对象是 RenderConstrainedBox ,会在构造时将 constraints 约束对象传入。


RenderConstrainedBoxperformLayout 中进行布局,可以看出当 child 非空时,会先执行 child 渲染对象的 layout ,并将 _additionalConstraints 和自身的 constraints 使用 enforce 方法进行叠加,作为子组件的约束。子组件布局玩后,自身的尺寸等于 child 渲染对象的尺寸。

这也就是,父渲染对象将约束自上而下传递给子节点,子渲染对象将尺寸自下而上 赋予父节点的原理。到这里大家应该对布局约束有了更深的认识,看那本文到这里就结束了,谢谢观看,明天见~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值