在 Flutter 当中,我们如何控制组件的大小?套上一层 Container?SizeBox?还是一些别的技巧?
有没有同学遇到这种情况:在布局中,无论如何都控制不了一个组件的大小,烦的一批?
例如这段代码:
Widget build(BuildContext context) {
return Scaffold(
body: ListView.separated(itemBuilder: (context, index){
return Container(
height: 200,
width: 200,
color: Colors.amber,
);
}, itemCount: 10, separatorBuilder: (BuildContext context, int index) {
return Container(height: 10,);
},),
);
}
我创建了一个ListView,每一个 item 是一个 200*200 的琥铂色(黄色)Container,讲道理,这个时候我们的脑海里就应该有这样的一个列表呈现。然而,当你运行的时候:
WTF??我 200*200 的大方块呢?怎么变成长方形了?
难道不应该是这样的吗?
好,带着这个问题,我们就来说今天的两个主角。
注意:这里并不打算深入讨论 Flutter 中约束的机制,只是为了讲解这两个控件。
看本篇文章时我们只需要记住:child 尺寸大小是由父级 Widget 给出的约束来调整的。
其他的如果想要了解,可以看这篇处理边界约束 (Box constraints) 的问题[1]
首先我们来说一下 ConstrainedBox
,从名字上我们也能解读个大概:「约束盒」。
没错,就是约束盒,他是用来干嘛的?看一下官方文档:
A widget that imposes additional constraints on its child. For example, if you wanted child to have a minimum height of 50.0 logical pixels, you could use const BoxConstraints(minHeight: 50.0) as the constraints. 在其 child 上添加约束的 widget。 例如,如果你希望 child 最小高度为 50.0 像素,可以用 BoxConstraints(minHeight: 50.0) 来进行约束。
接下来再看一下他的构造函数:
ConstrainedBox({
Key key,
@required this.constraints,
Widget child,
}) : assert(constraints != null),
assert(constraints.debugAssertIsValid()),
super(key: key, child: child);
可以看到,必要的参数有 constraints: BoxConstraints
,这里也简单说一下约束:
约束是由最小宽度、最大宽度、最小高度、最大高度四个方面构成;尺寸大小则由特定的宽度和高度两个方面构成。
那我们再来看一下 BoxConstraints
是怎么用的,它有6个构造函数,分别是:
1.BoxConstraints({double minWidth: 0.0, double maxWidth: double.infinity, double minHeight: 0.0, double maxHeight: double.infinity })
:使用给定的约束来构建2.BoxConstraints.expand({double width, double height })
:创建一个充满另一个约束的约束3.BoxConstraints.expand({double width, double height })
:创建一个不能大于给定大小的约束4.BoxConstraints.tight(Size size)
:创建一个给定大小的约束5.BoxConstraints.tightFor({double width, double height })
:同上6.BoxConstraints.tightForFinite({double width: double.infinity, double height: double.infinity })
:创建给定大小的约束,除非他们是无限的
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Container(
height: 200,
width: 200,
color: Colors.amber,
),
),
);
}
这是一个 200200 的Container,接下来定义一个 ConstrainedBox
,并且把min宽高设定为 300\300:
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: ConstrainedBox(
child: Container(
height: 200,
width: 200,
color: Colors.amber,
), constraints: BoxConstraints(minHeight: 300, minWidth: 300),
),
),
);
}
可以看到,这个时候 Container 的宽高完全是按照 ConstrainedBox
的约束来设定的。
该组件就厉害了,官方解释为:
A widget that imposes no constraints on its child, allowing it to render at its "natural" size. This allows a child to render at the size it would render if it were alone on an infinite canvas with no constraints. This container will then attempt to adopt the same size, within the limits of its own constraints. If it ends up with a different size, it will align the child based on alignment[2]. If the box cannot expand enough to accommodate the entire child, the child will be clipped. 对 child 不添加任何约束,允许他以 “自然”大小进行渲染。 这样一来,child就可以在没有约束的、无限的画布上进行渲染,然后此容器讲尝试在自身限制的范围内采用相同的大小,如果大小不相同,则根据 alignment 来对齐,如果child过大,则会裁剪 child。
在我们开头举的例子,为什么我设置 200*200 的Container默认是屏幕宽度的?
因为ListView这种类型的组件会根据滑动方向来设置约束。
例如垂直滑动,那么所有 item 的宽度都是默认 double.infinity
,所以这个时候无论我们设置多少宽度都是无用的,
相反也一样。
所以,如果我们想自己设定 item 的宽度,就需要用到 UnconstrainedBox
,不给child 设置约束。
这两个组件实际开发过程中使用的可能不是很多,但是了解一下肯定是要的,否则遇到类似的问题也很麻烦。
总结来讲,这些都属于 Flutter 约束中的知识点,只要有个了解就好了。
[1]
处理边界约束 (Box constraints) 的问题: https://flutter.cn/docs/development/ui/layout/box-constraints
[2]
alignment: https://api.flutter.dev/flutter/widgets/UnconstrainedBox/alignment.html