flutter第十六篇:布局原理
https://docs.flutter.dev/ui/layout/constraints
Constraints go down. Sizes go up. Parent sets position.
每个组件的布局都要经历四步:
第一步,组件从父组件处获知自己的约束,约束就是尺寸范围
第二步,组件依次告诉自己的子组件他们的约束是什么,并询问他们期望的尺寸
第三步,组件依次放置自己的子组件
第四步,组件告诉父组件自己的尺寸
例如,想要在屏幕中展示一个包含两个child的Column

布局过程如下:
1、Column从父组件处获知自己宽度最多300px,高度最多85px
2、Column预留5px作为padding,那么其child宽度最多是290px,高度最多是75px
3、Column告诉child1,child1的宽度最多是290px,高度最多是75px
4、child1向Column回复说自己期望宽290px,高20px
5、Column想把child2放在child1下面,那么child2的宽度最多是290px,高度最多是55px
6、Column告诉child2,child2的宽度最多是290px,高度最多是55px
7、child2向Column回复说自己期望宽140px,高30px
8、Column决定child1的左上角的坐标是(5, 5),宽290px,高20px,child2的左上角的坐标是(80, 25),宽140px,高30px
9、Column向父组件回复自己的尺寸期望是宽300px,高60px(5px+20px+30px+5px)
组件只能要求在约束范围内的尺寸。组件不能决定自己的位置。
组件由其底层的RenderBox渲染。box可分为三类:
第一类,尺寸尽可能的大,例如Center和ListView底层的box
第二类,和子组件的尺寸一样,例如Transform和Opacity底层的box
第三类,固定尺寸,例如Image和Text底层的box
Container的尺寸与其构造参数有关,默认是尽可能大,但如果指定了宽或高,那么宽或高会是固定尺寸。而Row和Column的尺寸与其约束有关。
Screen会强制其子组件与屏幕的尺寸完全相同,即使子组件要求的尺寸没那么大。
ConstrainedBox用于对其子组件添加约束。如一个组件的宽度为10,用ConstrainedBox包裹这个组件,设置最小宽度为50,那么这个组件的宽度会变为是50。但ConstrainedBox是根组件(最外层组件)的话,其constraints属性不会生效。
如果想把某个组件来自父组件的约束给去掉,那么可以用UnconstrainedBox包裹这个组件,这样的话,这个组件的大小就完全就取决于它自己了。UnconstrainedBox有alignment属性,值默认是Alignment.center,所以其child默认是居中的。
ConstrainedBox、UnconstrainedBox使用如下:
@override
Widget build(BuildContext context) {
return Align(
alignment: Alignment.center,
child: Container(
color: Colors.red,
width: 300,
height: 300,
alignment: Alignment.center,
child: ConstrainedBox(
constraints: const BoxConstraints(minWidth: 100, maxWidth: 200, minHeight: 150, maxHeight: 200),
child: Container(width: 60, height: 10, color: Colors.blue)),
));
}
外层红色Container的宽高是300,如果内层蓝色Container不用ConstrainedBox包裹的话,那么其宽高为60、10,用ConstrainedBox包裹后,其宽高变为100、150。
在上例的基础上,用UnconstrainedBox包裹内层蓝色Container,如下
@override
Widget build(BuildContext context) {
return Align(
alignment: Alignment.center,
child: Container(
color: Colors.red,
width: 300,
height: 300,
alignment: Alignment.center,
child: ConstrainedBox(
constraints: const BoxConstraints(minWidth: 100, maxWidth: 200, minHeight: 150, maxHeight: 200),
child: UnconstrainedBox(
child: Container(width: 60, height: 10, color: Colors.blue),
)),
));
}
被UnconstrainedBox包裹后,内部蓝色Container不再受父组件的约束,尺寸完全取决于自己,故宽高变为60、10。
想通过用SizedBox包裹组件来自定义其尺寸时,如果SizedBox的宽高不生效,则大概率是这个组件已经从它某个父组件那里收到了约束,此时我们可以在SizedBox外面套一个UnconstrainedBox,这样的话,SizedBox的宽高就会生效了。
如果想让一个组件尺寸超出父组件尺寸时不报错,则可以用OverflowBox包裹这个组件。如用一个UnconstrainedBox包裹一个超级宽的Container,如宽度为10000,因为用UnconstrainedBox包裹了,所以Container不用考虑其原来父组件(如果没有,则是屏幕)的约束,尺寸完全取决于自己,故宽为10000,在手机上会报溢出错误。如果换做用OverflowBox包裹这个Container,则Container的宽度最多等于屏幕宽度,不会报溢出错误。即使Container的child在左上角或右上角,也能正常展示出来。应用报溢出错误时,要想到用OverflowBox去解决。
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: OverflowBox(
child: Container(
color: Colors.red,
width: 1000,
height: 50,
alignment: Alignment.topLeft,
child: const Text("abc"),
),
),
);
}
LimitedBox用于指定最大宽高。如果想限制一个组件的宽高,则可以把这个组件用LimitedBox包裹起来。比如,一个宽、高分别为1000的Container,用LimitedBox包裹后,若设置maxWidth、maxHeight分别为100,则这个Container的宽高分别为100。
如果想对一个组件进行缩放,那么可以用FittedBox包裹这个组件。比如,有一个Text,里面文字很多,但我们不想让它换行,又想让所有文字都展示出来,那么可以用FittedBox包裹这个Text组件。
@override
Widget build(BuildContext context) {
return const Scaffold(
body: Center(
child: FittedBox(
child: Text(
'This is some very very very large text that is too big to fit a regular screen in a single line.',
),
),
),
);
}
浙公网安备 33010602011771号