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.',
          ),
        ),
      ),
    );
  }

 

posted on 2025-06-19 00:11  koushr  阅读(20)  评论(0)    收藏  举报

导航