title: flutter-布局
date: 2023-01-28 09:24:21.796
updated: 2023-02-02 09:12:35.782
url: https://hexo.start237.top/archives/flutter-布局
categories:
- IT技术
tags: - flutter
约束、尺寸、位置
Container(
color:Colors.red,
child: Container(
color:Colors.white,
width: 100,height: 100,
),
)
Container(
color:Colors.red,
width: 400,height: 400,
// child: Container(
// color:Colors.white,
// width: 100,height: 100,
// ),
)
Container(
color:Colors.red,
width: 400,height: 400,
child: Container(
color:Colors.white,
width: 100,height: 100,
child: FlutterLogo(),
),
)
以上的现象说明了在flutter的一个概念叫做 布局约束
void main() {
runApp(Container(
width: 200,
height: 200,
color: Colors.red,
));
}
void main() {
runApp(Container(
width: 200,
height: 200,
color: Colors.red,
child: Container(
width: 13,height: 13,
color: Colors.white,
),
));
}
void main() {
runApp(Container(
width: 1,
height: 1,
color: Colors.red,
child: Container(
width: 13,height: 13,
color: Colors.white,
),
));
}
void main() {
runApp(Container(
color: Colors.red,
child: Center(
child: Container(
width: 13,height: 13,
color: Colors.white,
),
),
));
}
[外链图片转存中…(img-n9EIjYoI-1685520466864)]
以深度优先的原则,向下传递约束,向上传递尺寸。
总结:
flutter从runApp
方法会传来一个由操作系统来的尺寸约束,没有丝毫的移动空间。下级元素设置的宽度和高度,都必须要满足上一级的尺寸约束。如果没有满足的话,就会被自动修正,可以理解为上一级给的建议,如果没有满足那么就会被采纳。
开始向下传递的就是紧约束,子级尺寸必段占满屏幕。从上到下传递的是约束,遇到位置组件时,会给尺寸的约束范围。
获取和设置布局约束
body: Container(
width: 400,height: 400,
color: Colors.redAccent,
child: LayoutBuilder(
builder: (BuildContext _,BoxConstraints constraints){
print(constraints.toString()); // BoxConstraints(w=375.2, h=400.0)
return FlutterLogo(size: 200,);
},
),
),
body: Container(
width: 400,height: 400,
color: Colors.redAccent,
child: Center(
child: LayoutBuilder(
builder: (BuildContext _,BoxConstraints constraints){
print(constraints.toString()); // BoxConstraints(0.0<=w<=375.2, 0.0<=h<=400.0)
return FlutterLogo(size: 200,);
},
),
),
)
方法 SizedBox.expand
会尽量填满上级的尺寸。
body: Container(
width: 400,height: 400,
color: Colors.redAccent,
child: Center(
child: SizedBox.expand(
// width: 200,height: 200,
child: LayoutBuilder(
builder: (BuildContext _,BoxConstraints constraints){
print(constraints.toString());
return FlutterLogo(size: 200,);
},
),
),
),
)
body: Container(
width: 300,height: 300,
color: Colors.redAccent,
child: FractionallySizedBox(
alignment: Alignment.topCenter,
widthFactor: 0.2,
heightFactor: 0.2,
child: LayoutBuilder(
builder: (BuildContext _,BoxConstraints constraints){
print(constraints.toString());
return FlutterLogo(size: 200,);
},
),
),
)
ConstrainedBox
child: Center(
child: ConstrainedBox(
constraints: const BoxConstraints(
maxHeight: 100,minHeight: 30,
maxWidth: 100,minWidth: 30,
),
child: LayoutBuilder(
builder: (BuildContext _,BoxConstraints constraints){
print(constraints.toString()); //BoxConstraints(30.0<=w<=100.0, 30.0<=h<=100.0)
return FlutterLogo(size: 330,);
},
),
),
)
child: Center(
child: ConstrainedBox(
constraints: const BoxConstraints(
maxHeight: 100,minHeight: 30,
maxWidth: 100,minWidth: 30,
).loosen(),
child: LayoutBuilder(
builder: (BuildContext _,BoxConstraints constraints){
print(constraints.toString()); //BoxConstraints(0.0<=w<=100.0, 0.0<=h<=100.0)
return FlutterLogo(size: 330,);
},
),
),
)
child: Center(
child: ConstrainedBox(
constraints: const BoxConstraints(
).tighten(width: 120,height: 120),
child: LayoutBuilder(
builder: (BuildContext _,BoxConstraints constraints){
print(constraints.toString());
return FlutterLogo(size: 330,);
},
),
),
)
带上padding后,约束尺寸成了BoxConstraints(0.0<=w<=280.0, 0.0<=h<=280.0)。padding起到了欺上瞒下的作用。
body: Container(
width: 300,height: 300,
color: Colors.redAccent,
child: Center(
child: Padding(
padding: const EdgeInsets.all(10),
child: LayoutBuilder(
builder: (BuildContext _,BoxConstraints constraints){
print(constraints.toString()); // BoxConstraints(0.0<=w<=280.0, 0.0<=h<=280.0)
return const FlutterLogo(size: 330,);
},
),
),
),
)
总结:
- 使用LayoutBuilder的组件可以获取到上一级组件的尺寸约束范围。
- 使用SizedBox.expand。会撑满上级的尺寸。
- FractionallySizedBox 组件可以根据父级尺寸的比例设置宽高,来约束下一级的组件尺寸。
- ConstrainedBox 组件可以设置一个最大和最小尺寸约束范围,使子元素不超过这个范围。
- ConstrainedBox中的constraints的属性值的 BoxConstraints.loosen()这个方法可以将最小范围调整为0,做成松约束。
- ConstrainedBox中的constraints的属性值的BoxConstraints.tighten()可以指定尺寸做成紧约束。可以使用 BoxSized的组件直接设置更加方便。
- 如果带上Pading的话,那么约束值会减小,向上传递的是原来的尺寸。起到欺上瞒下的作用。
Flex弹性布局
Column
Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceAround,
crossAxisAlignment: CrossAxisAlignment.start,
children: const [
Text("123fasfadsfasdfsdasdfadsfadsfasdfadsfasdf"),
FlutterLogo(size: 100,),
FlutterLogo(size: 50,)
],
)
属性 | 作用 |
---|---|
mainAxisSize | 主轴方向占据的空间有max和min |
mainAxisAlignment | 子元素主轴方向对齐方式 |
crossAxisAlignment | 子元素交叉轴方向对齐方式 |
CrossAxisAlignment.stretch
拉伸 这个值是对下级的约束变成紧约束,使按钮拉伸。
MainAxisSize.max
Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
ElevatedButton(onPressed: (){
}, child: Text("")),
Text("123fasfadsfasdfsdasdfadsfadsfasdfadsfasdf"),
FlutterLogo(size: 100,),
FlutterLogo(size: 50,)
],
)
Column的父级有一个Container设置了宽高或着设置了constraints约束属性的时候,那么对Column就会形成紧约束。在设置mainAxisSize时会没有意义。但是如果设置的是constraints属性会有一定的范围。
Container(
color:Colors.red,
constraints: const BoxConstraints(
maxWidth: 400,minWidth: 100,
maxHeight: 300,minHeight: 200
),
child: Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
ElevatedButton(onPressed: (){
}, child: Text("")),
Text("123fasfadsfasdfsdasdfadsfadsfasdfadsfasdf"),
FlutterLogo(size: 100,),
FlutterLogo(size: 50,)
],
),
)
Expanded 弹性组件
Column的子元素,用来分配剩余的空间。属性值flex
可以用来控制分配空间的比例。
Flexible 弹性组件
Column的子元素,Expanded继承自Flexible
Column在给下级传递约束时,由于不能提前知道子元素的高度。所以会提给一个无界的约束,使用LayoutBuilder
的组件查看,会打印出一个是正无穷的无界
约束的信息 0.0<=h<=Infinity
。
Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
LayoutBuilder(builder: (_,c){
print("button--->$c"); // button--->BoxConstraints(0.0<=w<=375.0, 0.0<=h<=Infinity)
return ElevatedButton(onPressed: (){
}, child: const Text(""));
}),
Expanded(
flex: 1,
child: Container(color:Colors.yellow)),
Text("123fasfadsfasdfsdasdfadsfadsfasdfadsfasdf"),
FlutterLogo(size: 100,),
Flexible(
flex: 2,
child: Container(color: Colors.blueGrey,)),
LayoutBuilder(builder:(_,c){
print("layoutbuilder-->$c");
return const FlutterLogo(size: 50,);
})
],
)
如果在Column中直接使用ListView会报错。因为Column中首先给没有弹性的组件是一个无限的高度约束,那么ListView也是无限的高度。所以会出现问题。因为ListView里面的children里面也是无限的高度。为了解决这个问题呢,需要在ListView外面包裹一层 Expanded
Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
const FlutterLogo(size:60),
ListView(
children: [
FlutterLogo(size: 120,),
],
),
const FlutterLogo(size: 60,)
],
)
Stack层叠组件
越往下写的组件,层叠的顺序越靠上。
在Stack中的子组件分为两大类 有位置组件
和 无位置组件
Stack的尺寸只会选择无位置组件
的最大尺寸的那个组件的尺寸做为自身的组件尺寸。
如果所有的组件都是有位置的话,那么他会铺满全屏。
都是无位置组件的话,会尽量的把自己缩小一些。
Stack(
children: const [
FlutterLogo(size: 20,),
Text("you have 123 jfdads fdasjfadsfadskj fdasf"),
FlutterLogo(size: 30,),
],
)
使用Alignment(-0.9,1)
这个属性值可以精确的控制子元素的位置。
Stack(
alignment: const Alignment(-0.9,1),
children: const [
FlutterLogo(size: 20,),
FlutterLogo(size: 30,),
Text("you have 123 jfdads fdasjfadsfadskj fdasf"),
],
)
使用Positioned
的组件,可以更加精确的控制子元素的位置。
Stack(
alignment: const Alignment(-0.9,1),
children: [
FlutterLogo(size: 20,),
FlutterLogo(size: 30,),
Text("you have 123 jfdads fdasjfadsfadskj fdasf"),
Positioned(
right: 0,
top:0,
child: Container(
width: 20,height: 20,color: Colors.blue,
)
)
],
)
属性值 fit:StackFit.loose
的意思是宽松的约束。
如果值是StackFit.expand
那么子元素会满足上级的约束,里面的Container会不受设置宽高的约束。
StackFit.passthrough
会满足上级约束。
元素溢出处理属性:clipBehavior
值: hardEdge
默认值裁切
值:none
不处理
Container为什么不听话
Container中如果不设置宽度和高度,那么元素会尽量满足上级的尺寸约束,如果有child的话,那么会适应子元素的尺寸。
Container实际上就是集合了很多个组件组成的。是个语法糖,是为了避免把代码写的像金字塔一样,所以用Container集合了很多属性,实际的Contaienr的源码中就是将这些属性的wigdet做了集合,帮我们写了这个金字塔。
在Column中的Container如果没有添加弹性组件,也没有写尺寸,也没有Child的话,那么这个Container的宽高就是0想当于以下这个组件。源码中也有这个组件的名称。
LimitedBox
这个组件可以设定maxHeight
这个属性值,如果上级约束是无界的话,那么这个maxHeight就是其组件的高度,如果上级约束是有界的话,那么这个组件就会使用上级的约束。类似于下面这个组件
Placeholder
组件里面有个属性叫 fallbackHeight
如果上级的约束是无界的话,那么组件的高度就会设置fallbackHeight为这个属值的值。
CustomMultiChildLayout wigdet
这个wigdet可以很方便的布局子组件
CustomMultiChildLayout(
delegate: MyDelegate(), //代理委托,见下方代码
children: [
LayoutId( // LayoutId可以给wigdet一个id
id:1,
child: const FlutterLogo(),
),
LayoutId(
id:2,
child: const FlutterLogo(),
)
],
)
class MyDelegate extends MultiChildLayoutDelegate {
// 在这个函数里面渲染安排组件位置
void performLayout(Size size) {
// TODO: implement performLayout
var size1;
var size2;
if(hasChild(1)){ // 判断是否有1这个id的组件
size1 =layoutChild( // 部局函数
1,
const BoxConstraints(maxHeight: 100,minHeight: 100,maxWidth: 100,minWidth: 100)
// BoxConstraints().tighten(width: 100,height: 100) 也可以传递一个紧约束。
// BoxConstraints().loosen() 这是一个松约束
);
positionChild(1, Offset(0,0)); // 设置位置组件
}
if(hasChild(2)){
size2 = layoutChild(
2,
const BoxConstraints(maxHeight: 100,minHeight: 100,maxWidth: 100,minWidth: 100)
);
positionChild(2, Offset(size1.width,size1.height));
}
}
bool shouldRelayout(_) =>true;
}