Flutter 布局多个widgets
垂直和水平放置多个widget
最常见的布局模式之一是垂直或水平排列widget。您可以使用行(Row)水平排列widget,并使用列(Column)垂直排列widget。
重点是什么?
- 行和列是两种最常用的布局模式。
- 行和列都需要一个子widget列表。
- 子widget本身可以是行、列或其他复杂widget。
- 您可以指定行或列如何在垂直或水平方向上对齐其子项
- 您可以拉伸或限制特定的子widget.
- 您可以指定子widget如何使用行或列的可用空间.
要在Flutter中创建行或列,可以将一个widget列表添加到Row 或Column 中。 同时,每个孩子本身可以是一个Row或一个Column,依此类推。以下示例显示如何在行或列内嵌套行或列。
此布局按行组织。该行包含两个孩子:左侧的一列和右侧的图片:
左侧的Column widget树嵌套行和列。
您将在嵌套行和列中实现一些Pavlova(图片中的奶油水果蛋白饼)的布局代码
注意:行和列是水平和垂直布局的基本、低级widget - 这些低级widget允许最大化的自定义。Flutter还提供专门的,更高级别的widget,可能足以满足您的需求。 例如,您可能更喜欢ListTile而不是Row,ListTile是一个易于使用的小部件,具有前后图标属性以及最多3行文本。您可能更喜欢ListView而不是列,ListView是一种列状布局,如果其内容太长而无法适应可用空间,则会自动滚动。 有关更多信息,请参阅通用布局widget。
对齐 widgets
您可以控制行或列如何使用mainAxisAlignment和crossAxisAlignment属性来对齐其子项。 对于行(Row)来说,主轴是水平方向,横轴垂直方向。对于列(Column)来说,主轴垂直方向,横轴水平方向。
MainAxisAlignment 和CrossAxisAlignment 类提供了很多控制对齐的常量.
注意: 将图片添加到项目时,需要更新pubspec文件才能访问它们 - 此示例使用Image.asset
显示图像。 有关更多信息,请参阅此示例的pubspec.yaml文件, 或在Flutter中添加资源和图像。如果您使用的是网上的图片,则不需要执行此操作,使用Image.network
即可。
在以下示例中,3个图像中的每一个都是100像素宽。渲染盒(在这种情况下,整个屏幕)宽度超过300个像素, 因此设置主轴对齐方式为spaceEvenly,它会在每个图像之间,之前和之后均匀分配空闲的水平空间。
appBar: new AppBar(
title: new Text(widget.title),
),
body: new Center(
child: new Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
new Image.asset('images/pic1.jpg'),
Dart code: main.dartImages: imagesPubspec: pubspec.yaml
列的工作方式与行相同。以下示例显示了一列,包含3个图片,每个图片高100个像素。 渲染盒(在这种情况下,整个屏幕)的高度大于300像素,因此设置主轴对齐方式为spaceEvenly,它会在每个图像之间,上方和下方均匀分配空闲的垂直空间。
appBar: new AppBar(
title: new Text(widget.title),
),
body: new Center(
child: new Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
new Image.asset('images/pic1.jpg'),
Dart code: main.dartImages: imagesPubspec: pubspec.yaml
*注意: 如果布局太大而不适合设备,则会在受影响的边缘出现红色条纹。例如,以下截图中的行对于设备的屏幕来说太宽:
通过使用Expanded widget,可以将widget的大小设置为适和行或列,这在下面的调整 widgets 部分进行了描述。
调整 widget
也许你想要一个widget占据其兄弟widget两倍的空间。您可以将行或列的子项放置在Expandedwidget中, 以控制沿着主轴方向的widget大小。Expanded widget具有一个flex属性,它是一个整数,用于确定widget的弹性系数,默认弹性系数是1。
例如,要创建一个由三个widget组成的行,其中中间widget的宽度是其他两个widget的两倍,将中间widget的弹性系数设置为2:
appBar: new AppBar(
title: new Text(widget.title),
),
body: new Center(
child: new Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
new Expanded(
child: new Image.asset('images/pic1.jpg'),
),
new Expanded(
flex: 2,
child: new Image.asset('images/pic2.jpg'),
),
new Expanded(
Dart code: main.dartImages: imagesPubspec: pubspec.yaml
要修复上一节中的示例:其中一行有3张图片,行对于其渲染框太宽,并且导致右边出现红色条中的问题,可以使用Expanded widget来包装每个widget。 默认情况下,每个widget的弹性系数为1,将行的三分之一分配给每个小部件。
appBar: new AppBar(
title: new Text(widget.title),
),
body: new Center(
child: new Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
new Expanded(
child: new Image.asset('images/pic1.jpg'),
),
new Expanded(
child: new Image.asset('images/pic2.jpg'),
),
new Expanded(
聚集 widgets
默认情况下,行或列沿着其主轴会尽可能占用尽可能多的空间,但如果要将孩子紧密聚集在一起,可以将mainAxisSize设置为MainAxisSize.min。 以下示例使用此属性将星形图标聚集在一起(如果不聚集,五张星形图标会分散开)。
class _MyHomePageState extends State<MyHomePage> {
@override
Widget build(BuildContext context) {
var packedRow = new Row(
mainAxisSize: MainAxisSize.min,
children: [
new Icon(Icons.star, color: Colors.green[500]),
new Icon(Icons.star, color: Colors.green[500]),
new Icon(Icons.star, color: Colors.green[500]),
new Icon(Icons.star, color: Colors.black),
new Icon(Icons.star, color: Colors.black),
],
);
// ...
}
嵌套行和列
布局框架允许您根据需要在行和列内部再嵌套行和列。让我们看下面红色边框圈起来部分的布局代码:
红色边框部分的布局通过两个行来实现。评级行包含五颗星和评论数量。图标行包含三列图标和文本。
评级行的widget树:
该ratings变量创建一个包含5个星形图标和一个文本的行:
class _MyHomePageState extends State<MyHomePage> {
@override
Widget build(BuildContext context) {
//...
var ratings = new Container(
padding: new EdgeInsets.all(20.0),
child: new Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
new Row(
mainAxisSize: MainAxisSize.min,
children: [
new Icon(Icons.star, color: Colors.black),
new Icon(Icons.star, color: Colors.black),
new Icon(Icons.star, color: Colors.black),
new Icon(Icons.star, color: Colors.black),
new Icon(Icons.star, color: Colors.black),
],
),
new Text(
'170 Reviews',
style: new TextStyle(
color: Colors.black,
fontWeight: FontWeight.w800,
fontFamily: 'Roboto',
letterSpacing: 0.5,
fontSize: 20.0,
),
),
],
),
);
//...
}
}
提示: 为了最大限度地减少由嵌套严重的布局代码导致的视觉混淆,可以在变量和函数中实现UI的各个部分。
评级行下方的图标行包含3列; 每个列都包含一个图标和两行文本,您可以在其widget树中看到:
该iconList变量定义了图标行:
class _MyHomePageState extends State<MyHomePage> {
@override
Widget build(BuildContext context) {
// ...
var descTextStyle = new TextStyle(
color: Colors.black,
fontWeight: FontWeight.w800,
fontFamily: 'Roboto',
letterSpacing: 0.5,
fontSize: 18.0,
height: 2.0,
);
// DefaultTextStyle.merge可以允许您创建一个默认的文本样式,该样式会被其
// 所有的子节点继承
var iconList = DefaultTextStyle.merge(
style: descTextStyle,
child: new Container(
padding: new EdgeInsets.all(20.0),
child: new Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
new Column(
children: [
new Icon(Icons.kitchen, color: Colors.green[500]),
new Text('PREP:'),
new Text('25 min'),
],
),
new Column(
children: [
new Icon(Icons.timer, color: Colors.green[500]),
new Text('COOK:'),
new Text('1 hr'),
],
),
new Column(
children: [
new Icon(Icons.restaurant, color: Colors.green[500]),
new Text('FEEDS:'),
new Text('4-6'),
],
),
],
),
),
);
// ...
}
}
该leftColumn变量包含评分和图标行,以及描述Pavlova的标题和文字:
class _MyHomePageState extends State<MyHomePage> {
@override
Widget build(BuildContext context) {
//...
var leftColumn = new Container(
padding: new EdgeInsets.fromLTRB(20.0, 30.0, 20.0, 20.0),
child: new Column(
children: [
titleText,
subTitle,
ratings,
iconList,
],
),
);
//...
}
}
左列放置在容器中以约束其宽度。最后,用整个行(包含左列和图像)放置在一个Card内构建UI:
Pavlova图片来自 Pixabay ,可以在Creative Commons许可下使用。 您可以使用Image.network直接从网上下载显示图片,但对于此示例,图像保存到项目中的图像目录中,添加到pubspec文件, 并使用Images.asset。 有关更多信息,请参阅在Flutter中添加Asserts和图片。
body: new Center(
child: new Container(
margin: new EdgeInsets.fromLTRB(0.0, 40.0, 0.0, 30.0),
height: 600.0,
child: new Card(
child: new Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
new Container(
width: 440.0,
child: leftColumn,
),
mainImage,
],
),
),
),
),
Dart code: main.dartImages: imagesPubspec: pubspec.yaml
提示: Pavlova示例在广泛的横屏设备(如平板电脑)上运行最佳。如果您在iOS模拟器中运行此示例, 则可以使用Hardware > Device菜单选择其他设备。对于这个例子,我们推荐iPad Pro。 您可以使用Hardware > Rotate将其方向更改为横向模式 。您还可以使用Window > Scale更改模拟器窗口的大小(不更改逻辑像素的数量)