• 最后,widget 将会把它的大小信息向上传递至父 widget(包括其原始约束条件)。

官方对话(构建一个带有padding的column):

Widget: “嘿!我的父级。我的约束是多少?”

Parent: “你的宽度必须在 80300 像素之间,高度必须在 3085 之间。”

Widget: “嗯…我想要 5 个像素的内边距,这样我的子级能最多拥有 290 个像素宽度和 75 个像素高度。”

Widget: “嘿,我的第一个子级,你的宽度必须要在 0290,长度在 075 之间。”

First child: “OK,那我想要 290 像素的宽度,20 个像素的长度。”

Widget: “嗯…由于我想要将我的第二个子级放在第一个子级下面,所以我们仅剩 55 个像素的高度给第二个子级了。”

Widget: “嘿,我的第二个子级,你的宽度必须要在 0290,长度在 055 之间。”

Second child: “OK,那我想要 140 像素的宽度,30 个像素的长度。”

Widget: “很好。我的第一个子级将被放在 x: 5 & y: 5 的位置,而我的第二个子级将在 x: 80 & y: 25 的位置。”

Widget: “嘿,我的父级,我决定我的大小为 300 像素宽度,60 像素高度。”

什么是约束?

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

约束Constraints 在Flutter中是一种_布局协议_,Flutter中有两大布局协议BoxConstraintsSliverConstraints。对于非滑动的控件例如Padding,Flex等一般都使用_BoxConstraints盒约束_。

约束可以分为:宽松约束(loose)和 严格约束(tight) 就widget布局树而言,约束默认向下传递,可有些布局类复写了performLayout改变了约束行为,将向下传递的约束信息改变甚至重建,从而导致有些布局行为变得不可预测,因此就需要掌握一些常用布局的布局原理,才能对整体的布局行为加以分析。

案例分析


案例一:Container的约束失效

ConstrainedBox(

constraints: BoxConstraints.tightFor(

width: double.infinity,

height: double.infinity),

child: Container(width: 100, height: 100, color: red)

)

上边的案例,展示了一个预期宽100,高100的红色方块,可实际上只会得到一个满屏幕的红色。 虽然Container内部有宽高的约束,但是在这里是不起作用的,究其原因,是因为Container内部的布局特性引起的,父部件传递一个tight紧布局,而到了Container由于内部使用的ConstrainedBox,其布局行为会做如下改变:

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

最后

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助

因此我收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点!不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
…(img-0nuyH9bf-1715559560336)]

[外链图片转存中…(img-nwJtJExN-1715559560338)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点!不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐