1. 為什么Container設(shè)置width和height不生效?
下面這段代碼瞄崇,Container設(shè)置了(width: 100, height: 100),但渲染出來是填充滿屏幕的婿屹,這是為什么?
class HomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(width: 100, height: 100, color: Colors.red);
}
}
可以從Container的初始化和build方法看下具體原因:
Container({
double? width,
double? height,
...
}) :
constraints =
(width != null || height != null)
? constraints?.tighten(width: width, height: height)
?? BoxConstraints.tightFor(width: width, height: height)
: constraints,
super(key: key);
@override
Widget build(BuildContext context) {
Widget? current = child;
if (color != null)
current = ColoredBox(color: color!, child: current);
if (constraints != null)
current = ConstrainedBox(constraints: constraints!, child: current);
return current!;
}
從Container初始化可以看出constraints是一個(gè)緊約束,寬高是100。
通過build方法生成ColoredBox和ConstrainedBox,其對(duì)應(yīng)的渲染類為_RenderColoredBox和RenderConstrainedBox溢十。其對(duì)應(yīng)關(guān)系如下:
flutter約束有下面特點(diǎn):
可以理解RenderConstrainedBox的約束是從父節(jié)點(diǎn)向下傳遞截粗,而大小是從_RenderColoredBox向上傳遞信姓。
constraints是怎么來的鸵隧?
constraints是通過RenderView.performLayout傳遞逐級(jí)傳遞下來的,此時(shí)的constraints是一個(gè)緊約束,寬高就是屏幕的高度。
_additionalConstraints是RenderConstrainedBox初始化傳遞進(jìn)來的約束意推,也就是開始設(shè)置的(100,100)
_additionalConstraints.enforce(constraints)是決定了當(dāng)前size的大小:
//BoxConstraints
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),
);
}
_additionalConstraints是Container的約束(100,100),constraints是屏幕傳遞下來的寬度和高度(414,896)豆瘫。_additionalConstraints.enforce(constraints)最后產(chǎn)生的結(jié)果就是屏幕的高度和寬度。而這個(gè)約束繼續(xù)傳遞給_RenderColoredBox進(jìn)行繪制顏色,這也解釋了為什么Container設(shè)置了寬高不生效的原因菊值。
2. 修改上面代碼,再想想結(jié)果是什么外驱?
class HomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Center(
child: Container(width: 100, height: 100, color: Colors.red),
);
}
}
直接看下Center繼承自Align,其對(duì)應(yīng)的RenderObject是RenderPositionedBox,而performLayout方法做了什么?
@override
void performLayout() {
...
if (child != null) {
child!.layout(constraints.loosen(), parentUsesSize: true);
...
} else {
...
}
child.layout傳遞的是constraints.loosen(),
BoxConstraints(
minWidth: 0.0,
maxWidth: maxWidth,
minHeight: 0.0,
maxHeight: maxHeight,
)
maxWidth和maxHeight是double.infinity,也就是(100,100).enforce(constraints)返回是(100,100)腻窒。這就很好的解釋了為什么Center包裹之后child就生效了昵宇。
如果了解約束的原理,下面的兩個(gè)題應(yīng)該很容易得出結(jié)果:
ConstrainedBox(
constraints: const BoxConstraints(
minWidth: 70,
minHeight: 70,
maxWidth: 150,
maxHeight: 150,
),
child: Container(color: Colors.red, width: 10, height: 10),
)
Center(
child: ConstrainedBox(
constraints: const BoxConstraints(
minWidth: 70,
minHeight: 70,
maxWidth: 150,
maxHeight: 150,
),
child: Container(color: red, width: 1000, height: 1000),
),
)
總結(jié):
約束是從父類向下傳遞的,在計(jì)算自身layout時(shí)會(huì)用自身的約束與父類約束做一個(gè)收縮儿子,只有當(dāng)子約束的最大最小高度和寬度都是在父控件的范圍內(nèi)才生效瓦哎。否則以父約束的臨界點(diǎn)生效。