简介

对于一个前端框架来说,除了各个组件之外,最重要的就是将这些组件进行连贯的布局了。布局的英文名叫做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/

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

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