5、Flutter布局原理 :为什么我的部件不听话

一、你是否有这样的疑惑?

  1. 位置、大小飘忽不定?

为什么我的Widget位置/大小跟我想象的不一样?一会儿在这,加个child就跑到另外一个地方,或父Widget突然变大或变小了?这TMD该怎么布局啊。

  1. 想改变一个Widget的大小,不知如何下手?

我是从iOS平台转过来的,之前用数字精确定位视图的位置,现在它不给我设置数字的机会。或者使用约束,方便的很…

  1. 面对一个页面无从下手?

出现以上问题,大概率是因为不明白flutter的布局原理和声明式编程范式。本系列文章将从“Flutter 设计者的角度”来解释“为什么Flutter的布局会是这样的”。OK, let`s go。

二、住建部和开发商

举个例子:A县城为了响应“建设美丽家乡”政策,决定对县城进行改造。住建部画出一块1000亩的地作为科技板块交给一个靠谱的开发商开发。为了防止项目烂尾,住建部提出了3点要求:

  1. 必须合理安排布局,不能浪费土地资源不能烂尾;
  2. 所有的建筑必须经过审批,不能擅自开发。
  3. 板块内建筑可以自由设计,不能脱离该板块

开发商深刻领悟了会议精神,并总结如下:

  1. 必须用完这1000亩,不能多,更不能少(小心diao脑袋)。
  2. 建筑师的设计总建筑面积只要小于 1000亩即可,空出来的地做绿化带

最终项目顺利完成,A县被评为最美家乡称号。
我们来分析一下这个例子中的三个角色:
住建部:给开发商传递的一项硬性的指标:面积必须是1000亩;多了,少了都没法向上交代,对吧?

开发商:在给出的方案也必须是1000亩才能通过审批,多了少了都不行,否则就拿不到这个项目了。

设计师:拿到的指标是面积小于等于1000亩,在1000亩以内,他可以自由发挥,因此他的方案只要在这个范围之内就可以。
这个例子中,住建部->开发商 ->设计师 提出要求,本质是上层对下层传递一种约束条件,设计师->开发商->住建部提交方案审核是下层向上层传递的是具体的方案,正是因为这种向下,向上的沟通方式,项目才得以完成。

flutter布局是就是的这个方式。
Flutter不希望开发者开发出来的页面是有手机屏幕的一半,也不能超出屏幕让用户看不到。当然相信你也不会像让你的app像下面图示一样:

因此,Flutter规定“App的页面必须占满整个屏幕”。记住了,是“必须”,没有一丝的商量余地。App页面里面的内容,只要不超过屏幕大小就可以了,大小你自己定

假设现在我们有一部手机,屏幕尺寸是320 * 480,Flutter给页面的约束就是:width = 320, height = 480,页面返回的大小肯定比必须是320 * 480。(不然就强制拉伸或报错)

如果要在页面中中间布局一张20 x 20的图片,页面给他的子部件传递的约束通常是是:0 < width <= 320, 0 < height < 480,然后图片会将位置信息:(x,y, width, height)交给上一级审批,然后由上级把它放在合适的位置。
**

这个过程也就是Flutter中的布局原理“向下传递约束,向上传递大小”。

**

二、代码演示

1、页面的约束
我们直接在RunApp中布局一个100 * 100 的 Container,看下他的约束是什么。通过LayoutBuilder可以查看约束。

void main() {
  runApp(
      LayoutBuilder(builder: (context, constraints){
        /// 通过LayoutBuilder 查看约束
        print(constraints);
        return Container(
          width: 100,
          height: 100,
          color: Colors.red,
        );
      })
  );
}

输出信息如下:

... //省略无关信息
flutter: BoxConstraints(w=430.0, h=932.0)
... //省略无关信息

可以看到Flutter给我们的约束是:w=430.0, h=932.0。翻译过来就是宽度必须是430, 高度必须是932。看一下实际效果:

可以看到我们设置的宽度=100和高度=100违反了上级的约束,所以不会生效。像这种width=xx,height=xx的约束,称为强约束因为它不可违背(结合开头的例子:住建部的规定,不能违背)。

2、子部件的约束
接下来我们在Container内部放一个50 x 50的FlutterLogo,看看什么效果。

void main() {
runApp(LayoutBuilder(builder: (context, constraints) {
 /// 通过LayoutBuilder 查看约束
 print('页面约束$constraints');
 return Container(
   alignment: Alignment.center,
   color: Colors.red,
   child: LayoutBuilder(
     builder: (context, constraints) {
       print('子部件约束$constraints');
       return FlutterLogo(size: 50,);
     },
   ),
 );
}));
}

打印信息如下:

... //省略无关信息
flutter: 页面约束BoxConstraints(w=430.0, h=932.0)
flutter: 子部件约束BoxConstraints(0.0<=w<=430.0, 0.0<=h<=932.0)
... //省略无关信息

可以看到子部件获得的约束是0.0<=w<=430.0, 0.0<=h<=932.0,是一个范围,而不是一个具体的值。那我们的FlutterLogo大小是满足这个约束的,因此FlutterLogo的大小应该是50x50。我们来看下效果:

像这种0.0<=w<=430.0, 0.0<=h<=932.0宽高是一个范围的约束成为松约束

如果打破约束,我们将宽高设为1000x1000会怎样?代码改为 return FlutterLogo(size: 1000,);

查看一下它的尺寸:实际上是430x932,也就是说“约束不可被打破”(除非强制打破约束,后续会讲)。
在这里插入图片描述
再来捋一下它的流程:父部件Container向子部件FlutterLogo传递约束0.0<=w<=430.0, 0.0<=h<=932.0,子部件根据约束布局了1000x100的大小告诉了父部件,父部件发现超过了约束,自动修正为最大尺寸。(当然如果超过了最小值,也会纠正)

三、Container的大小如何确定

首先我们要了解设计Container的初衷是什么?Container翻译过来时容器,容器是为了包裹住一些东西。我们来思考2个问题:
1、容器里什么都不放:容器应高设置为符合约束的最大值还是最小值?如果设置为最小值0,那么我们是不是看不见了,如果是最大值,我们是能看到的。
2、容器里放了一个子部件,容器应该是取子部件的最大值还是最小值?如果是最大值,我们写代码的时候是不是要每个容器都要加一堆这是大小的代码? 如果是最小值,会不会更好?

这里先告诉大家答案。

1、没有子部件时:Container会取约束的最大值;
2、有子部件是:Container会取约束的最小值;

其实也好理解:无子部件时,取最大值能让开发者看到效果,有子部件时,减少了控制大小的代码量,主要是为了方便。换句话说“也可以设置成其他值,只不过这样方便”。
验证代码如下:
(1)有子部件情况

void main() {
  runApp(LayoutBuilder(builder: (context, constraints) {
    /// 通过LayoutBuilder 查看约束
    print('页面约束$constraints');
    return Container(
      alignment: Alignment.center,
      color: Colors.red,
      child: LayoutBuilder(
        builder: (context, constraints) {
          print('子部件约束$constraints');
          return Container(
            color: Colors.green,
              child: FlutterLogo(size: 100,)
          );
        },
      ),
    );
  }));
}

效果:

(2)无子部件情况

void main() {
  runApp(LayoutBuilder(builder: (context, constraints) {
    /// 通过LayoutBuilder 查看约束
    print('页面约束$constraints');
    return Container(
      alignment: Alignment.center,
      color: Colors.red,
      child: LayoutBuilder(
        builder: (context, constraints) {
          print('子部件约束$constraints');
          return Container(
            color: Colors.green,
              // child: FlutterLogo(size: 100,)
          );
        },
      ),
    );
  }));
}

效果如下:

四、总结

1、Flutter中布局原理用一句话概括为“向下传递约束,向上传递大小”。
2、约束氛围紧约束和松约束:width = xx, height = xxx的是紧约束, xx < width < yy, xx < height < yy 的是松约束。
3、Container布局分为有子部件和无子部件情况:
有子部件时,取子部件的大小,无子部件时,取符合约束的最大值。
下期预告 :通过CustomMultiChildLayout 演示布局原理&自定义布局

最后欢迎大家留言,一起交流,一起成长。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值