@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)
分为两种情况:
-
父布局传递constraints的hasBoundedWidth为true,这种情况下,maxWidth是肯定失效的
-
父布局必须是宽松布局才行,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的布局行为符合预期。
===================================================================
约束确定后,想要绘制上屏,还缺一个很重要的属性——位置
子部件的大小是通过父类传递的约束来确定的,同样的,位置也是由父部件确定,以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学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门,即可获取!