大家好,我是 17。
如果你是 web 前端开发,你一定会这样的体会, weight,height 都是作用于 element 本身
<div style='width:100px;height:100px; ></div>
在页面中,这个 div 的高宽一定是 100 高 100 宽的。带着这样的认识,当你学习 flutter的时候,很容易会觉得
container 的 width ,height,也是这样的作用。但事实上,container 的 width ,height 只是约束,container 实际的尺寸是由约束,孩子的尺寸,和本身的规则一起决定的。
flutter 中的布局遵循这样的原则 constrains go down ,sizes go up. Parent sets position. 直译过来就是 constrains 向下传递,sizes 向上传递。parent 决定尺寸。
Constrains
Constrains 是抽象类,不能直接用,我们实际用的是 BoxConstrains。看看 BoxConstrains 的构造函数
const BoxConstraints({
this.minWidth = 0.0,
this.maxWidth = double.infinity,
this.minHeight = 0.0,
this.maxHeight = double.infinity,
})
一共有四个值。默认值的意思是尺寸可以随意,没有限制。特别的,最大最小值相同的 contains 称为 tight constrains,最小值为0 的 contains 称为 loose contains。一般来说,只要最大最小不相同的都可以看成是 loose constrains。
下面我会举例子,不用 container ,因为它是一个复合 widget,比较复杂。因为是研究 contrains,所以只用最简单的 widget。第一个例子会给出完整代码,后面的例子只给出关键代码。
我是用 chrome,renderView 调整为 500 x 296,具体多少不重要,你只要知道看到这个数值就是满屏就行了。
例一
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
Widget build(BuildContext context) {
return const DecoratedBox(decoration: BoxDecoration(color: Colors.green));
}
}
把上面的代码 copy 到 main.dart中,执行,会发现全屏都是绿色。其间发生了如下的沟通过程。
- app 告诉 DecoratedBox, 你的尺寸必须是 500 x 296
- DecoratedBox 显示为 500 x 296
app 是如何告诉 DecoratedBox 的呢,是传 constrains 给 DecoratedBox
constraints: BoxConstraints(w=500.0, h=296.0)
例二
const Center(child: DecoratedBox(decoration: BoxDecoration(color: Colors.green)));
当用 Center 包装后,什么都不会显示。这涉及到 DecoratedBox 自身的规则,它会尽量小。
- app 告诉 Center, 你的尺寸必须是 500 x 296
- Center 告诉 DecoratedBox 你可以显示为任意尺寸,只要别超过 500 x 296
- DecoratedBox 最小可以显示为 0 ,所以我们就看不见了。
Center 传给 DecoratedBox 的约束
constraints: BoxConstraints(0.0<=w<=500.0, 0.0<=h<=296.0)
Center 继承 自 Align。 Align 的一个功能就是把 tight 约束 转为 loose 约束
例三
Center(
child: ConstrainedBox(
constraints: const BoxConstraints(minWidth: 100, minHeight: 100),
child: const DecoratedBox(
decoration: BoxDecoration(color: Colors.green))));
DecoratedBox 获得了最小尺寸约束,所以它只好显示为 100 x 100
每次都写全四个属性有点麻烦,所以 ConstrainedBox 给出了快捷构造函数,拿 tight举例,其它就不再赘述了。
BoxConstraints.tight(Size size)
Size只有两个参数,width,height
const Size(double width, double height)
用 BoxConstraints.tight可以构造出 最大最小相等的约束。
BoxConstraints.tight(Size(width:100,height:100)
等价于
BoxConstraints(minWidth: 100, minHeight: 100,maxHeight: 100,maxWidth: 100)
例四
const Center(
child: SizedBox(
width: 100,
height: 100,
child: DecoratedBox(
decoration: BoxDecoration(color: Colors.green))));
用 SizedBox 可以达到同样的效果,而且代码更简洁些。和 ConstrainedBox 相比,SizedBox 只能构造 tight 约束。
例五
const Center(
child: SizedBox(
width: 100,
height: 100,
child:SizedBox(
width: 300,
height:300,
child: DecoratedBox(
decoration: BoxDecoration(color: Colors.green))))
);
可能你会认为 box 会显示为 300 x 300,但实际上是 100 x 100,这是因为 SizedBox,ConstrainedBox 把综合父级和自身的 contains ,处理后传给子级。
因为父级传过来的是 tight 约束 SizedBox,ConstrainedBox 会忽略自身的约束。所以显示为 100 x100
例六
Center(
child: ConstrainedBox(
constraints:const BoxConstraints(maxHeight: 100),
child:const SizedBox(
width: 300,
height:300,
child: DecoratedBox(
decoration: BoxDecoration(color: Colors.green))))
);
如果父级传过来的是 loose 约束,可以在父级允许的范围内修改.
- Center 传给 BoxConstraints
constraints: BoxConstraints(0.0<=w<=500.0, 0.0<=h<=296.0)
- BoxConstraints 传给 SizedBox
constraints: BoxConstraints(0.0<=w<=500.0, 0.0<=h<=100.0)\
- SizedBox 传给 BoxDecoration
BoxConstraints(w=300.0, h=100.0)
综合 例五 例六,我们可以得出结论
BoxConstraints,sizedBox 只能在父级允许的范围内做修改。
例七
UnconstrainedBox(
child: DecoratedBox(decoration: BoxDecoration(color: Colors.green)));
}
- app 传给 UnconstrainedBox
constraints: BoxConstraints(w=500.0, h=296.0)
- UnconstrainedBox 传给 DecoratedBox
constraints: BoxConstraints(unconstrained)
UnconstrainedBox的作用是去掉 contains。BoxDecoration 在没有任何约束的情况下,会显示为最小,也就是 0 x 0 ,所以我们看不见它了。
例八
Directionality(
textDirection: TextDirection.ltr, child: Row(children: const [
LimitedBox(maxWidth: 100,maxHeight: 100,
child: SizedBox(width: 300,height: 300,
child: DecoratedBox(decoration: BoxDecoration(color: Colors.green)),
),
)
]));
Directionality 是 row 必须的上级,不然报错。Directionality 不处理 constraints,所以 app直接把 constraints传给了 Row
- app 传给 Row
constraints: BoxConstraints(w=500.0, h=296.0)
- Row 修改宽度的 contain 为不限并传给 LimitedBox
constraints: BoxConstraints(0.0<=w<=Infinity, 0.0<=h<=296.0)
修改宽度为 Infinity 是 Row 本身的算法。
- LimitedBox 修改 maxWidth 并传给 DecoratedBox
constraints: BoxConstraints(0.0<=w<=100.0, 0.0<=h<=296.0)
代码中 LimitedBox 修改了宽高,为什么只有 宽生效呢?
LimitedBox 只能修改 值 为 Infinity 的约束。
- SizedBox 加上自己的约束传给 DecoratedBox
constraints: BoxConstraints(w=100.0, h=296.0)
SizedBox 想达到高宽 都为 300 但还得满足在父级允许的范围内。所以 传给 DecoratedBox 的只能是 100 x 296
最终 DecoratedBox 显示为 100 x 296
和 Row 类似,Column会修改高度的 constrain 为 Infinity。
在 Row,Column 中 为了避免错误,一定要注意给children 增加约束。
在前面的例子中, 为了做纯的 constrains 研究,没有用 Container,因为 Container 有自己的特性,如果不知 constrains 的原理,会被它的特性误导,误以为是 constrains 起作用。下面举几个 Container 的例子,说说 Container的特性。
例九
Center(
child: Container(
color: Colors.green,
));
如果直接写Container( color: Colors.green)
会全屏绿好理解,因为是 app 要求 Container必须要显示为全屏,但是现在已经加了Center了啊。为什么还是全屏? 这个就是Container的特性之一,当没有孩子的时候,会尝试尽可能能的大。
例十
Center(
child: Container(
width: 100,
color: Colors.green,
));
例九中有一个词埋下了伏笔。“尝试”尽可能的大。“尝试”结果就是没有孩子,没有约束,尽可能的大,有约束就按约束来。
本例中,高度没有约束,所以占满屏幕,宽度有约束,显示为 100
例十一
Center(
child: Container(
color: Colors.red[300],
child: Container(
width: 100,
height: 100,
color: Colors.green,)));
明明外面还有一个红色的 Container,但怎么看不见呢。这时展示了 Container的另一个特性,当它有孩子的时候,尝试尽可能的小。
本例中,因为 Center 让 红色 Container 可以任意大小,只要不超过屏幕,但它有孩子 ,所以它决定要变得和孩子一样大。幸运的是孩子也没有超过屏幕,于是它就和孩子一样大了。但因为孩子的绿色覆盖了它的红色,所以我们看不到它了。
例十一
Center(
child: Container(
color: Colors.red[300],
padding: const EdgeInsets.all(20),
child: Container(
width: 100,
height: 100,
color: Colors.green,
)));
当有pading的时候,就可以看到 红色的。特别的,这时的红色Container展示为 140 x 140
在没有约束的情况下 padding 越大,Container 越大,但如果是有约束呢?
例十二
Center(
child: Container(
color: Colors.red[300],
padding: const EdgeInsets.all(20),
child: Container(
padding: const EdgeInsets.all(60),
width: 100,
height: 100,
color: Colors.green,
child: Container(
width: 50,
height: 50,
color: Colors.blue[400],
),
)));
最里面加了一个 蓝色 Container,但是完全看不到,因为 Container的高宽是包含padding的,绿色Container的高宽为 100,padding 却点了 120,占了所有的空间,所以没有蓝色 Container的显示空间了。padding超出不会报错,超出按最大值算。
作为最后一例,详细解说一下
- app 告诉 Cetner ,你必须 为屏幕大小
- Cetner 告诉 红色 Container 你可以任意大小,但别超过屏幕
- 红色 Container 发自己有 padding ,所以它告诉 绿色 Container ,你可以任意大小,但别超过 (500 - 40) x (296 - 40),因为屏幕的高宽为 500 x 296 ,各减去 padding 40,也就是 460 x 256
- 绿色 Container 同减去 Padding 后告诉 蓝色 Container ,只能显示为 0 x 0
- 蓝色 Container 报告说,好的,我显示 为 0 x 0
- 绿色 Container,报告给 红色 Container 说 ,我显示为 100 x 100
- 红色 Container 报告给 Center 说, 我显示为 460 x 256
- Center 于是 把 红色 Container 放在中间,并上报给 app,我显示为全屏。
如果你完全理解了上面的全部,你就有了布局的坚实的基础了。虽然还有很多布局的 Widget 没有介绍,但原理已经解释了,其它的都可以举一反三。最后还特意介绍了 Container ,以便区分哪些是 约束的效果,哪些是 Container 自己的行为,这样才能更好的理解约束。
如果你觉得有所收获,给我点个贊吧~