Flutter布局指南之Box套盒子

2022-03-31 21:51:36 浏览数 (1)

对于写过Flutter的开发者来说,我敢肯定,大部分的开发者都不能准确预测这次Hot Reload之后,布局是否是自己想要的结果。Flutter的布局与Native的布局方式非常不同,所以,了解Flutter这茫茫多的布局组件,是我们准确布局的基础。

在Flutter中,有一堆Box布局组件,它们可以用来更加精确的调整布局,下面我们就来看看这些Box都有哪些作用。

ConstrainedBox

ConstrainedBox用于限制Child Widget的尺寸约束,例如:

  • 让Text最宽100,从而实现多行
  • 固定Widget最大最小尺寸
代码语言:javascript复制
body: Center(
  child: Column(
    mainAxisAlignment: MainAxisAlignment.center,
    children: [
      ConstrainedBox(
        constraints: const BoxConstraints(
          maxWidth: 100,
        ),
        child: const Text(
          'xuyisheng',
          textAlign: TextAlign.center,
          style: TextStyle(
            fontSize: 50,
          ),
        ),
      ),
      ConstrainedBox(
        constraints: const BoxConstraints(
          minHeight: 100,
        ),
        child: ElevatedButton(
          child: const Text('Flutter'),
          onPressed: () {},
        ),
      ),
    ],
  ),
)

效果如图所示。

UnconstrainedBox

UnconstrainedBox的作用正好和ConstrainedBox相反。它可以破除组件本身的约束规则,从而更方便的进行布局。

例如下面这个例子:

代码语言:javascript复制
Center(
  child: Container(
    color: Colors.blue,
    width: 300,
    height: 300,
    child: Container(
      width: 100,
      height: 100,
      color: Colors.red,
    ),
  ),
)

由于Container的布局规则,内部的Container被设置为父Widget尺寸,从而忽略了子Widget的尺寸设置,所以,这里使用UnconstrainedBox来解除这种约束:

代码语言:javascript复制
Center(
  child: Container(
    color: Colors.blue,
    width: 300,
    height: 300,
    child: UnconstrainedBox(
      child: Container(
        width: 100,
        height: 100,
        color: Colors.red,
      ),
    ),
  ),
)

从而可以让内部的Container按照自身尺寸进行布局。

更加灵活一点,我们还可以选择保留某一方向上的约束:constrainedAxis: Axis.horizontal。

SizedBox

SizedBox有下面几个使用场景:

  • 当你需要一个确切尺寸的Widget时,通过SizedBox来进行约束
  • 在父容器中撑满剩余空间
  • 在没有child的情况下,对空间做分割

场景1:

代码语言:javascript复制
SizedBox(
  width: 200,
  height: 200,
  child: Text(
    'xuyisheng',
    textAlign: TextAlign.center,
    style: TextStyle(
      fontSize: 50,
    ),
  ),
)

场景2:某方向上的double.infinity,会在父级允许的尺寸下尽可能多的拓展。

代码语言:javascript复制
Center(
  child: Container(
    color: Colors.red,
    width: 200,
    height: 200,
    child: Center(
      child: Container(
        color: Colors.blue,
        child: const SizedBox(
          width: double.infinity,
          height: 100,
          child: Text(
            'xuyisheng',
            textAlign: TextAlign.center,
            style: TextStyle(
              fontSize: 50,
            ),
          ),
        ),
      ),
    ),
  ),
)

展示效果如图所示。

如果width和height方向上都是撑满父Widget的剩余空间,那么可以使用SizedBox.expand来简写。

还有一个便捷方法——SizedBox.shrink,它的作用是让尺寸在父容器的约束下尽可能的小,如果父容器不设置minWidth或者minHeight,那么它的尺寸就是0,这个属性通常和BoxConstraints一起配合使用。

FractionallySizedBox

这是Flutter给你提供的一个百分百布局工具。通常用于在父容器中,按照百分比来进行布局。

代码语言:javascript复制
Center(
  child: Container(
    color: Colors.red,
    width: 200,
    height: 200,
    child: const FractionallySizedBox(
      widthFactor: 0.5,
      heightFactor: 0.5,
      child: Text(
        'xuyisheng',
        textAlign: TextAlign.center,
        style: TextStyle(
          fontSize: 50,
        ),
      ),
    ),
  ),
)

展示效果如图所示。

和SizedBox一样,它也可以用于作为Widget直接的间隔,只不过它使用的是百分比作为单位,总量是父容器的尺寸。

如果使用Flexible组件包裹FractionallySizedBox,那么就可以适用于Row和Column。

要注意的是,widthFactor和heightFactor是可以大于1的,也就是说,子Widget可以超出父容器展示。

LimitedBox

当Widget没有父级来限制它们的尺寸时,如何在Widget上设置它的默认大小呢?这就需要使用到LimitedBox了。

LimitedBox只在父容器没有提供尺寸约束时,对子Widget的尺寸进行默认约束,在在Listview和Column、Row中是非常有用的。

❝如果外部容器对Child设置了尺寸约束,那么LimitedBox将不会生效 ❞

例如下面这个场景:

代码语言:javascript复制
ListView(
  children: [
    for (var i = 0; i < 100; i  )
      Container(
        margin: const EdgeInsets.all(8),
        color: Colors.green,
      ),
  ],
)

由于Listview中无尺寸约束,所以Container是不会展示出来的,这时候就需要使用LimitedBox。

代码语言:javascript复制
ListView(
  children: [
    for (var i = 0; i < 100; i  )
      LimitedBox(
        maxHeight: 100,
        child: Container(
          margin: const EdgeInsets.all(8),
          color: Colors.green,
        ),
      ),
  ],
)

一句话总结LimitedBox的作用:在不受限制的环境中,为其子元素提供默认尺寸。

FittedBox

在Flutter中,Widget之间可以任意堆叠、嵌套,所以,当子Widget的尺寸与父Widget尺寸不一致时,就会产生一些奇怪的样式,FittedBox就是用来处理这种场景的。

代码语言:javascript复制
Center(
  child: Container(
    width: 200,
    height: 200,
    color: Colors.red,
    child: Text(
      'xuyishengxuyisheng',
      style: TextStyle(
        fontSize: 50,
      ),
    ),
  ),
)

效果如图所示。

Text会因为父容器尺寸的限制而自动换行,下面我们给它加上FittedBox。

代码语言:javascript复制
Center(
  child: Container(
    width: 200,
    height: 200,
    color: Colors.red,
    child: FittedBox(
      child: Text(
        'xuyishengxuyisheng',
        style: TextStyle(
          fontSize: 50,
        ),
      ),
    ),
  ),
)

效果如图所示。

可以发现,FittedBox默认的fit是contain,所以内容被完整的一行显示了,与FontSize无关,这个就可以很方便的自适应修改文字大小。

当然,你还可以设置别的fit方式,详细的可以参考Flutter Dojo中的例子。fit属性是非常有用的一个属性,例如当我们设置FittedBox后,文字会在设备中自动显示为一行,但是在横竖屏切换时,Text会自动修改字体大小,来适配contain的效果,如果你想让它保存当前的文字Size,那么可以设置Fit为scaleDown,这样的话,它就会以最小尺寸来进行适配,当空间足够的时候,就不会自动放大字体大小了。

FittedBox中还可以设置alignment,从而控制剩余空间中子Widget的对齐方式。

简而言之,FittedBox就是一个让Child可以适配Parent的组件。

Flexible

准确来说,Flexible不算是Box类布局容器,但它和Box布局方式息息相关,所以这里一起说了。

Flexible通常在Column或者Row中使用,借助Flexible,可以让Column和Row中的元素根据Flex比例进行布局。

代码语言:javascript复制
Column(
  children: [
    Flexible(
      flex: 3,
      child: Container(
        color: Colors.cyan,
      ),
    ),
    Flexible(
      flex: 2,
      child: Container(
        color: Colors.green,
      ),
    ),
    Flexible(
      flex: 1,
      child: Container(
        color: Colors.purple,
      ),
    ),
  ],
)

同时,当子Widget有尺寸约束时,可以使用fit属性来控制Flex选择怎样的约束,如果是FlexFit.tight,那么Flexible将严格按照Flex布局,而忽略子Widget的尺寸约束,如果是FlexFit.loose,则会将尺寸设置为子Widget的尺寸。

OverflowBox

对于Flutter的子父Widget来说,子Widget一般都是限制于父Widget的尺寸约束之下,但如果一定要让子Widget超过父Widget的渲染区域,那么就可以通过OverflowBox来实现。

代码语言:javascript复制
Container(
  color: Colors.blue,
  width: 200,
  height: 200,
  padding: const EdgeInsets.all(12.0),
  child: OverflowBox(
    alignment: Alignment.topLeft,
    maxWidth: 300.0,
    maxHeight: 500.0,
    child: Container(
      color: Colors.red,
      width: 400.0,
      height: 400.0,
    ),
  ),
)

效果如图所示。

本文原创公众号:群英传,授权转载请联系微信,授权后,请在原创发表24小时后转载。

0 人点赞