关于flutter:flutter系列之UI-layout简介

3次阅读

共计 6220 个字符,预计需要花费 16 分钟才能阅读完成。

简介

对于一个前端框架来说,除了各个组件之外,最重要的就是将这些组件进行连贯的布局了。布局的英文名叫做 layout,就是用来形容如何将组件进行摆放的一个束缚。

在 flutter 中,基本上所有的对象都是 widget, 对于 layout 来说也不例外。也就是说在 flutter 中 layout 也是用代码来实现的,这和其余的用配置文件来形容 layout 的语言有所不同。

你能够把 layout 看做是一种看不见的 widget,这些看不见的 widget 是用来作用在可见的 widget 对象上,给他们施行一些限度。

flutter 中 layout 的分类

flutter 中的 layout widget 有很多,他们大略能够分为三类,别离是只蕴含一个 child 的 layout widget,能够蕴含多个 child 的 layout widget 和可滑动的 Sliver widgets。

这三种 layout 也有很多种具体的实现,对于 Single-child layout widgets 来说,蕴含上面这些 widgets:

  • Align — 用来对其蕴含在其中的组件进行对其操作。
  • AspectRatio — 对其中的组件进行比例缩放。
  • Baseline — 通过应用子组件的 baseline 来进行定位。
  • Center — 自组件位于两头。
  • ConstrainedBox — 相似于 IOS 中的 constrain, 示意子组件的限度条件。
  • Container — 一个罕用的 widget,能够用来蕴含多个其余的 widget。
  • CustomSingleChildLayout — 将其单个子项的布局推延。
  • Expanded — 将 Row, Column 或者 Flex 的 child 进行扩大。
  • FittedBox — 依据 fit 来缩放和定位其 child。
  • FractionallySizedBox — 将 child 依照总可用空间进行调整。
  • IntrinsicHeight — 一个将其 child 调整为 child 固有高度的小部件。
  • IntrinsicWidth — 一个将其 child 调整为 child 固有宽度的小部件。
  • LimitedBox — 限度一个 box 的 size。
  • Offstage — 将 child 放入 render tree 中,然而却并不触发任何重绘。
  • OverflowBox — 容许 child 笼罩父组件的限度。
  • Padding — 为 child 提供 padding。
  • SizedBox — 给定 size 的 box。
  • SizedOverflowBox — 能够笼罩父组件限度的 box。
  • Transform — 子组件能够变换。

以上是蕴含单个 child 的 layout 组件,上面是能够蕴含多个 child 的 layout 组件:

  • Column — 示意一列 child。
  • CustomMultiChildLayout — 应用代理来定位和缩放子组件。
  • Flow — 流式布局。
  • GridView — 网格布局。
  • IndexedStack — 从一系列的 child 中展现其中的一个 child。
  • LayoutBuilder — 能够依赖父组件大小的 widget tree。
  • ListBody — 依据给定的 axis 来布局 child。
  • ListView — 可滚动的列表。
  • Row — 示意一行 child。
  • Stack — 栈式布局的组件。
  • Table — 表格模式的组件。
  • Wrap — 能够对子 child 进行动静调整的 widget。

可滑动的 Sliver widgets 有上面几种:

  • CupertinoSliverNavigationBar — 是一种 IOS 格调的导航 bar。
  • CustomScrollView — 能够自定义 scroll 成果的 ScrollView。
  • SliverAppBar — material 格调的 app bar, 其中蕴含了 CustomScrollView。
  • SliverChildBuilderDelegate — 应用 builder callback 为 slivers 提供 child 的委托。
  • SliverChildListDelegate — 应用 list 来为 livers 提供 child 的委托。
  • SliverFixedExtentList — 固定 axis extent 的 sliver。
  • SliverGrid — child 是二维散布的 sliver。
  • SliverList — child 是线性布局的 sliver。
  • SliverPadding — 提供 padding 的 sliver。
  • SliverPersistentHeader — 可变 size 的 sliver。
  • SliverToBoxAdapter — 蕴含单个 box widget 的 Sliver。

罕用 layout 举例

下面咱们列出了所有的 flutter layout,他们简直满足了咱们在程序中会用到的所有 layout 需要,这里咱们以两个最根本和最罕用的 layout:Row 和 Column 为例,来具体解说 layout 的应用。

Row 和 Column 都属于下面讲到的多个 child 的 layout widget,它外面能够蕴含多个其余的 widget 组件。

先看一下 Row 和 column 的定义。

class Row extends Flex {
  Row({
    Key? key,
    MainAxisAlignment mainAxisAlignment = MainAxisAlignment.start,
    MainAxisSize mainAxisSize = MainAxisSize.max,
    CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.center,
    TextDirection? textDirection,
    VerticalDirection verticalDirection = VerticalDirection.down,
    TextBaseline? textBaseline, // NO DEFAULT: we don't know what the text's baseline should be
    List<Widget> children = const <Widget>[],}) : super(
    children: children,
    key: key,
    direction: Axis.horizontal,
    mainAxisAlignment: mainAxisAlignment,
    mainAxisSize: mainAxisSize,
    crossAxisAlignment: crossAxisAlignment,
    textDirection: textDirection,
    verticalDirection: verticalDirection,
    textBaseline: textBaseline,
  );
}
class Column extends Flex {
  Column({
    Key? key,
    MainAxisAlignment mainAxisAlignment = MainAxisAlignment.start,
    MainAxisSize mainAxisSize = MainAxisSize.max,
    CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.center,
    TextDirection? textDirection,
    VerticalDirection verticalDirection = VerticalDirection.down,
    TextBaseline? textBaseline,
    List<Widget> children = const <Widget>[],}) : super(
    children: children,
    key: key,
    direction: Axis.vertical,
    mainAxisAlignment: mainAxisAlignment,
    mainAxisSize: mainAxisSize,
    crossAxisAlignment: crossAxisAlignment,
    textDirection: textDirection,
    verticalDirection: verticalDirection,
    textBaseline: textBaseline,
  );
}

能够看到 Row 和 Column 都继承自 Flex,并且他们的构造方法都是调用了 Flex 的构造方法,两者的区别就在于 direction 不同,Row 的 direction 是 Axis.horizontal,而 Column 的 direction 是 Axis.vertical。

那么什么是 Flex 呢?

Flex 是一个 widget,在 Flex 中的子组件会依照某一个指定的方向进行展现。这个方向是能够管制的,比方横向或者竖向,如果你曾经提前晓得了主轴的方向,那么能够应用 Row 或者 Column 来代替 Flex,因为这样更加简洁。

在 Flex 中,如果想要 child 在某个方向填满可用空间,则能够将该 child 包装在 Expanded 中。

要留神的是,Flex 是不可滚动的,如果 Flex 中的 child 太多,超出了 Flex 中的可用空间,那么 Flex 将会报错,所以如果你须要展现很多 child 的状况下,能够思考应用可滚动的组件,比方 ListView。

如果你只有一个 child,那么就没有必要应用 Flex 或者 Row 和 Column 了,能够思考应用 Align 或者 Center 来对 child 进行定位。

在 Flex 中有几个十分重要的参数,比方 mainAxisAlignment 示意的是子组件沿主轴方向的排列规定,mainAxisSize 示意的是主轴的 size 大小,crossAxisAlignment 示意的是和主轴垂直轴的子组件排列规定。当然还有它最最重要的 children 属性,children 是一个 Widget 的 list 列表,用来存储要展现的子组件。

以 Row 为例,咱们创立一个简略的 RowWidget:

class RowWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Row(
      textDirection: TextDirection.ltr,
      children: [YellowBox(),
        YellowBox(),
        YellowBox(),],
    );
  }
}

这里咱们返回了一个 Row 对象,设置了 textDirection 和 children 属性。

children 外面是自定义的 YellowBox:

class YellowBox extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Container(
      width: 50,
      height: 50,
      decoration: BoxDecoration(
        color: Colors.yellow,
        border: Border.all(),),
    );
  }
}

YellowBox 是一个长和宽都是 50 的正方形。咱们这里应用了 BoxDecoration 对其上色。

最初将 RowWidget 放到 Scaffold 的 body 外面,如下所示:

  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text(widget.title),
      ),
      body: RowWidget());
  }

咱们能够看到上面的图像:

大家能够看到 YellowBox 是紧贴在一起的,如果咱们想要平均别离该如何做呢?

咱们能够在 Row 中增加一个属性叫做 mainAxisAlignment, 取值如下:

mainAxisAlignment: MainAxisAlignment.spaceEvenly

从新运行,生成的图像如下:

下面咱们还提到了一个 Expanded 组件,能够用来填充残余的可用空间,咱们把最初一个 YellowBox 用 Expanded 围起来,如下所示:

    return Row(
      textDirection: TextDirection.ltr,
      mainAxisAlignment: MainAxisAlignment.spaceEvenly,
      children: [YellowBox(),
        YellowBox(),
        Expanded(child: YellowBox(),
        )
      ],
    );

生成的图像如下:

能够看到最初一个 Box 填充到了整个 Row 残余的空间。

大家要留神,这时候 mainAxisAlignment 是没有成果的。

如果察看 Expanded 的构造函数,能够看到 Expanded 还有一个 flex 属性:

  const Expanded({
    Key? key,
    int flex = 1,
    required Widget child,
  }) : super(key: key, flex: flex, fit: FlexFit.tight, child: child);

flex 属性示意的是 flex factor, 默认值是 1,还是下面的例子,咱们将 flex 调整为 2,看看成果:

children: [YellowBox(),
        YellowBox(),
        Expanded(
          flex:2,
          child: YellowBox(),)
      ],

运行的后果和 flex= 1 是一样的,为什么呢?

事实上这个 flex 示意的是绝对于其余 Expanded 的组件所占用的空间比例。咱们能够讲所有的子组件都用 Expanded 进行裁减,而后再看看成果:

      children: [
        Expanded(child: YellowBox(),
        ),
        Expanded(child: YellowBox(),
        ),
        Expanded(
          flex: 2,
          child: YellowBox(),)
      ],

运行后果如下:

能够看到最初一个 child 占用的空间是后面两个的两倍。

如果咱们想要在 YellowBox 两头增加空格怎么办呢?有两种办法。

第一种办法是应用 SizedBox,如下:

children: [
        Expanded(child: YellowBox(),
        ),
        SizedBox(width: 100,),
        Expanded(child: YellowBox(),
        ),
        Expanded(
          flex: 2,
          child: YellowBox(),)
      ],

SizedBox 外面能够蕴含子 child,从而从新设置子 child 的长度和高度。如果不蕴含子 child 则会生成一个空格。

还有一种形式是应用 Spacer, 如下所示:

      children: [
        Expanded(child: YellowBox(),
        ),
        Spacer(flex: 2),
        Expanded(child: YellowBox(),
        ),
        Expanded(
          flex: 2,
          child: YellowBox(),)
      ],

生成的图像如下:

Spacer 和 SizedBox 都能够生成空白,不同的是 Spacer 能够和 flex 一起应用,而 SizedBox 必须固定 size 大小。

总结

以上就是 fluter 中 layout 和的分类和根本 layout Row 和 Column 的应用状况了。

本文的例子:https://github.com/ddean2009/learn-flutter.git

更多内容请参考 http://www.flydean.com/07-flutter-ui-layout-overview/

最艰深的解读,最粗浅的干货,最简洁的教程,泛滥你不晓得的小技巧等你来发现!

欢送关注我的公众号:「程序那些事」, 懂技术,更懂你!

正文完
 0