简介
对于一个前端框架来说,除了各个组件之外,最重要的就是将这些组件进行连贯的布局了。布局的英文名叫做 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/
最艰深的解读,最粗浅的干货,最简洁的教程,泛滥你不晓得的小技巧等你来发现!
欢送关注我的公众号:「程序那些事」, 懂技术,更懂你!