flutter-布局-flutter-布局

文章详细介绍了Flutter中的布局概念,包括Container的使用、布局约束的传递、LayoutBuilder在获取和设置尺寸约束中的作用。还探讨了SizedBox.expand、FractionallySizedBox、ConstrainedBox的用法,以及Column的弹性布局特性,如mainAxisAlignment和crossAxisAlignment。此外,文章提到了Expanded和Flexible组件在分配空间中的作用,以及Stack层叠组件的定位方法。最后,讨论了CustomMultiChildLayout自定义布局的可能性。
摘要由CSDN通过智能技术生成

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,);
              },
            ),
          ),
        ),
      )

总结:

  1. 使用LayoutBuilder的组件可以获取到上一级组件的尺寸约束范围。
  2. 使用SizedBox.expand。会撑满上级的尺寸。
  3. FractionallySizedBox 组件可以根据父级尺寸的比例设置宽高,来约束下一级的组件尺寸。
  4. ConstrainedBox 组件可以设置一个最大和最小尺寸约束范围,使子元素不超过这个范围。
  5. ConstrainedBox中的constraints的属性值的 BoxConstraints.loosen()这个方法可以将最小范围调整为0,做成松约束。
  6. ConstrainedBox中的constraints的属性值的BoxConstraints.tighten()可以指定尺寸做成紧约束。可以使用 BoxSized的组件直接设置更加方便。
  7. 如果带上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;

}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值