Flutter 布局真经

@override

// this.constraints 为父部件传递的约束

void performLayout() {

final BoxConstraints constraints = this.constraints; // this.constraints为父部件传递过来的约束

if (child != null) {

// 在这里改变了约束,可能会导致自身设置的约束失效

child!.layout(_additionalConstraints.enforce(constraints),

parentUsesSize: true);

size = child!.size;

} else {

size = _additionalConstraints.enforce(constraints).constrain(Size.zero);

}

}

// clamp方法会自行在约束范围境内选择

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),

);

}

按照先前的布局原理来说,Container告诉父布局自己需要一个100x100的空间,而父布局也有充分的空间提供,那预期的效果应该会很好的呈现,可在这里由于ConstrainedBox内部改变了布局行为,导致预期结果不生效,ConstrainedBox会通过enforce函数衡量自身的约束属性即 _additionalConstraints,和父布局传递的约束,在其中取临近值,在这里由于父部件约束为:

BoxConstraints(

minWidth: double.infinity,

maxWidth: double.infinity,

minHeight: double.infinity,

maxHeight: double.infinity,

);

故所以_additionalConstraints的约束行为会被改成double.infinity~double.infinity之间的值即double.infinity。

案例二:奇怪的LimitedBox

现有这样两个场景:

ConstrainedBox(

constraints: BoxConstraints.tightFor(

width: double.infinity,

height: double.infinity),

child: UnconstrainedBox(

child: LimitedBox(

maxWidth: 100,

child:

Container(color: Colors.red, width: double.infinity, height: 100),

),

)

)

ConstrainedBox(

constraints: BoxConstraints.tightFor(

width: double.infinity,

height: double.infinity),

child: Center(

child: LimitedBox(

maxWidth: 100,

child:

Container(color: Colors.red, width: double.infinity, height: 100),

),

)

)

两者的区别不是很明显,只是前者的LimitedBox由UnconstrainedBox包裹,而后由Center包裹,但两个例子所展示的UI却大相径庭:

这样看来是Center致使LimitedBox的maxWidth约束失效了,为什么会这样呢?

// LimitedBox的布局过程

BoxConstraints _limitConstraints(BoxConstraints constraints) {

return BoxConstraints(

minWidth: constraints.minWidth,

// 判断是否有边界,如果有,则maxWidth会失效

maxWidth: constraints.hasBoundedWidth ? constraints.maxWidth : constraints.constrainWidth(maxWidth),

minHeight: constraints.minHeight,

maxHeight: constraints.hasBoundedHeight

? constraints.maxHeight
constraints.constrainHeight(maxHeight),

);

}

@override

void performLayout() {

if (child != null) {

final BoxConstraints constraints = this.constraints;

child!.layout(_limitConstraints(constraints), parentUsesSize: true);

size = constraints.constrain(child!.size);

} else {

size = _limitConstraints(constraints).constrain(Size.zero);

}

}

bool get hasBoundedWidth => maxWidth < double.infinity;

引起LimitedBox的maxWidth失效的原因已经很明显了, 关键就在于这行代码:

constraints.hasBoundedWidth ? constraints.maxWidth : constraints.constrainWidth(maxWidth)

分为两种情况:

  1. 父布局传递constraints的hasBoundedWidth为true,这种情况下,maxWidth是肯定失效的

  2. 父布局必须是宽松布局才行,constraints.constrainWidth(maxWidth)会取constraints中的maxWidth临近值,所以紧布局也可能会使maxWidth失效。

所以这里LimitedBox的maxWidth约束是否失效的关键,在于父部件传递来的布局是否有边界,而Center和UnconstrainedBox在传递给子部件约束信息的处理上是有区别的: Center调用的constraints.loosen()方法,将当前约束进行松绑,而当前约束的hasBoundedWidth是不确定的!(依赖父部件)传递的约束。 UnconstrainedBox则重新构建的了一个无限大小的约束:childConstraints = const BoxConstraints(); 这样子部件是肯定可以收到一个hasBoundedWidth = false 并且为宽松特性的约束,所以在这里Center是有可能致使LimitedBox的maxWidth约束失效的,而UnconstrainedBox则可以百分百保证LimitedBox的布局行为符合预期。

位置的确定

===================================================================

约束确定后,想要绘制上屏,还缺一个很重要的属性——位置

ParentData


子部件的大小是通过父类传递的约束来确定的,同样的,位置也是由父部件确定,以RenderBaseline为例,

@override

void performLayout() {

if (child != null) {

final BoxConstraints constraints = this.constraints;

child!.layout(constraints.loosen(), parentUsesSize: true);

final double childBaseline = child!.getDistanceToBaseline(baselineType)!;

final double actualBaseline = baseline;

final double top = actualBaseline - childBaseline;

final BoxParentData childParentData = child!.parentData as BoxParentData;

childParentData.offset = Offset(0.0, top);

final Size childSize = child!.size;

size = constraints.constrain(Size(childSize.width, top + childSize.height));

} else {

performResize();

}

可以看到在子部件不为空的情况下,先对子部件传递约束,随之通过自身的特性,确定下一个Offset类型的位置点,然后传递给child的parentData,帮助child部件确定位置属性,当子部件进行绘制的时候,直接读取即可!

后续

================================================================

Flutter布局的基本原理无外乎父部件对子部件位置和大小的确定,不同类型的widget有不同的‘脾气’,会在传递约束以及位置的时候做出不同的更改。 不过,在Flutter中,布局还牵扯到很多其他功能,例如relayoutBoundary对于重绘的处理、部件点击事件的处理,这些都是和布局原理密不可分的,在搞懂布局原理的情况下,在去看这些实现,会轻松很多。

大家如果还想了解更多Android 相关的更多知识点,可以点进我的 GitHub项目中:https://github.com/733gh/Android-T3 自行查看,里面记录了许多的Android 知识点。最后还请大家点点赞支持下!!!



大家如果还想了解更多Android 相关的更多知识点,可以点进我的 GitHub项目中:https://github.com/733gh/Android-T3 自行查看,里面记录了许多的Android 知识点。最后还请大家点点赞支持下!!!

写在最后

由于本文罗列的知识点是根据我自身总结出来的,并且由于本人水平有限,无法全部提及,欢迎大神们能补充~

将来我会对上面的知识点一个一个深入学习,也希望有童鞋跟我一起学习,一起进阶。

提升架构认知不是一蹴而就的,它离不开刻意学习和思考。

**这里,笔者分享一份从架构哲学的层面来剖析的视频及资料分享给大家,**梳理了多年的架构经验,筹备近1个月最新录制的,相信这份视频能给你带来不一样的启发、收获。

最近还在整理并复习一些Android基础知识点,有问题希望大家够指出,谢谢。

希望读到这的您能转发分享和关注一下我,以后还会更新技术干货,谢谢您的支持!

转发+点赞+关注,第一时间获取最新知识点

Android架构师之路很漫长,一起共勉吧!
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》点击传送门,即可获取!
的架构经验,筹备近1个月最新录制的,相信这份视频能给你带来不一样的启发、收获。

[外链图片转存中…(img-b5cOZRZN-1714812225026)]

[外链图片转存中…(img-UT33DtyE-1714812225027)]

最近还在整理并复习一些Android基础知识点,有问题希望大家够指出,谢谢。

希望读到这的您能转发分享和关注一下我,以后还会更新技术干货,谢谢您的支持!

转发+点赞+关注,第一时间获取最新知识点

Android架构师之路很漫长,一起共勉吧!
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》点击传送门,即可获取!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值