注册

搞懂Flutter的布局约束

Flutter布局通常会出现一些奇怪的现象,例如,我希望一个Container是100宽高的正方形,于是我这样写


void main() => runApp(Home());

class Home extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
color: Colors.amber,
width: 100,
height: 100,
);
}
}

但是结果却是全屏.
image.png
感觉难以理解,出现这种情况的原因是,Flutter布局方式是对Widget Tree先进行自上而下对每个Widget进行约束(Constraint 过程),再自下而上决定每个Widget的大小(Size 过程),最后由父Widget决定每个子Widget的位置.总结下来就三步




  • 1,向下Constraint




  • 2,向上Size




  • 3,父Widget决定位置




这里我们来解释一下上面的代码为什么会白屏.
在第一步过程中屏幕是Container的父亲,屏幕给Container的约束是和屏幕一样大,在向上Size的过程中,这里我们必须理解的是,不是子Widget想多大就多大,它必须考虑它的约束后给出一个大小,这里虽然它是宽100,高100不过约束强制它全屏了,所以向上Size的时候它就是全屏大小,最后父亲也就是屏幕决定它的位置,也就是坐标(0,0).


我们改一下代码


void main() => runApp(Home());

class Home extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Center(
child: Container(
color: Colors.amber,
width: 100,
height: 100,
),
);
}
}

现在我们对Container包裹了一个Center,运行后是


image.png


现在和我们希望的样子一样了,因为现在的Center对于它子Widget的约束不是屏幕大小了,而是子Widget它希望自己多大就多大,最后Center决定Container的位置是居中,这样就看到了运行后的效果了。


这里可能会有个疑问是,为什么屏幕对于子Widget的约束是全屏幕大小,而Center对于它子Widget的约束却是它希望多大就多大呢?我怎么知道一个父Widget的是怎么约束子Widget的呢?


对于屏幕它是作为一个最上层的父亲,它给予第一个Widget(根Widget)的约束就是屏幕大小,无论根Widget怎么设置自己大小都授全屏约束。接下来Widget的约束就是根据具体情况具体定了(视Widget类型,剩余空间等影响,具体情况具体分析)


下面我们再来一个例子


void main() => runApp(Home());

class Home extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
child: Container(
color: Colors.amber,
width: 100,
height: 100,
),
);
}
}

运行后变成这样
image.png
又是全屏了,这里证明了Container对于子Widget约束是撑满的. 那么我不想撑满,除了之前提到的Center还有哪些方式呢?
1,使用Align
Align可以用来对齐一个Widget,同时它对子Widget的大小约束是子widget想多大就多大.(当然这里还需要满足Align父亲的约束).


void main() => runApp(Home());

class Home extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Align(
alignment: Alignment.centerLeft,
child: Container(
color: Colors.amber,
width: 100,
height: 100,
),
);
}
}

image.png
2,使用Stack


void main() => runApp(Home());

class Home extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Stack(
textDirection: TextDirection.ltr,
children: [
Container(
color: Colors.amber,
width: 100,
height: 100,
)
],
);
}
}

image.png


当然还有其它Widget是对子Widget的大小约束是子widget想多大就多大,具体这里就不一一列举了.


最后再来看一个Colum的例子,回顾下布局约束的整个流程.


void main() => runApp(Home());

class Home extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Column(
children: [
Container(
color: Colors.amber,
width: 100,
height: 100,
),
Container(
color: Colors.red,
width: 100,
height: 100,
),
Container(
color: Colors.blue,
width: 100,
height: 100,
),
],
);
}
}

image.png


我们来分析一遍


1,Column作为第一个Widget,自然是全屏.


2,在Column约束下,三个Container需要100的宽高,Column都能满足它们的要求


3,根据Column的布局特性决定三个Container的位置.


结语:


Flutter中Widget不能决定它自己的大小和位置,必须要结合父Widget的约束去决定,同样父Widget也有父Widget,所以我们在指定Widget的位置和大小的时候一定要把父Widget的约束考虑进去.


作者:屹森
来源:juejin.cn/post/7217770698784555064

0 个评论

要回复文章请先登录注册