欢迎来到 Flutter 布局 codelab!你将在这里学到如何构建 Flutter UI,更棒的是这一切都不需要安装 Flutter 或者 Dart!
重点提醒
这个 codelab 涵盖了 Flutter 的基本布局概念,并且将会使用一个叫做 DartPad 的实验性代码编辑器。DartPad 并没有在所有浏览器上进行严格测试,如果你在任何特定浏览器上使用 DartPad 遇到了问题,请创建一个 DartPad issue 并指明是哪个浏览器。
Flutter 与其他框架有着明显的差异,原因在于它使用代码来构建 UI,而不是 XML 或其他东西。其中,Widget 是构建 Flutter UI 的基本单元。当你逐渐深入这个 codelab,你将会发现在 Flutter 中几乎所有的东西都是 Widget。Widget 是一个不会改变的对象,它是 UI 中一个特定部分的描述。你还会学到 Flutter 的 Widget 非常容易组合,这意味着你能够通过组合已有的 Widgets 来创造更多复杂的 Widgets。到这篇文章的最后,你会运用这里所学的知识构建一个显示名片的 Flutter UI。
本 codelab 的预期完成时间约为 45 - 60 分钟
1. Row 和 Column 类
Row
和 Column
是两个用来容纳和布局 Widgets 的类。在它们内部的 Widgets 我们称为 children, Row
和 Column
就作为它们的父级。 Row
将会让 Widgets 水平排列,而 Column 则会让其竖直排列。
下面的样例将会显示 Row
和 Column
的区别。
1. 点击 运行 按钮。
Row
改为
Column
并再次运行。
2. 轴大小和对齐方式
BlueBox
widget 已经在一起被压扁了(在界面的左边或者上面)。你可以通过轴大小和对齐属性来改变
BlueBox
Widget 的间距。
Row
和
Column
分别占据了不同的主轴。
Row
的主轴是水平的。
mainAxisSize
决定了
Row
和
Column
能够在主轴上占据多大空间。
mainAxisSize
有两个可选属性:
MainAxisSize.max
Row
和
Column
占据它们主轴上所有空间。如果子 Widget 的总宽度小于主轴上的空间,它们就会充满剩余的空间。
MainAxisSize.min
Row
和
Column
仅占据它的 children 在主轴上所需的空间,它的 children 在主轴之间将没有额外空间。
mainAxisSize
默认为
MainAxisSize.max
。如果你不特别指定其他的值,就会使用默认值,就像我们前一个样例中展示的那样。
mainAxisSize
为其默认值
MainAxisSize.max
。
MainAxisSize.max
改为
MainAxisSize.min
,并再次运行。
mainAxisSize
被设为
MainAxisSize.max
,
Row
和
Column
将会使用额外空间来对齐它的 children。
mainAxisAlignment
属性决定了
Row
和
Column
将会在额外空间中如何对齐它的 children。
mainAxisAlignment
有以下六个可选属性:
MainAxisAlignment.start
Row
的起点在左边,
Column
的起点在顶部)
MainAxisAlignment.end
Row
的终点在右边,
Column
的终点在底部)
MainAxisAlignment.center
MainAxisAlignment.spaceBetween
MainAxisAlignment.spaceEvenly
MainAxisAlignment.spaceAround
MainAxisAlignment.spaceEvenly
相似,但在第一个 child 之前以及最后一个孩子之后减少了一半的空间,让其 children 之间宽度缩减一半。
mainAxisAlignment
为其默认值,
MainAxisAlignment.start
。
2. 将 MainAxisAlignment.start
改为 MainAxisAlignment
,然后再次运行。
MainAxisAlignment.end
换成其他值试试看。
crossAxisAlignment
属性决定了
Row
和
Column
能够如何在其横轴上定位 children。
Row
的横轴是竖直的,而
Column
则是水平的。绝大多数
crossAxisAlignment
属性仅在
Row
中生效。
crossAxisAlignment
属性有五个可选属性:
CrossAxisAlignment.start
Row
中生效)
CrossAxisAlignment.end
Row
中生效)
CrossAxisAlignment.center
Row
中生效)
CrossAxisAlignment.stretch
Row
中是从顶至底,
Column
则是从左至右)
CrossAxisAlignment.baseline
Text
类,并要求
textBaseline
属性设置为
TextBaseline.alphabetic
。
crossAxisAlignment
为其默认值,
CrossAxisAlignment.center
。
mainAxisAlignment
被设为
MainAxisAlignment.spaceAround
,
Row
现在包含一个比 “BlueBox” Widget 更高的
BiggerBlueBox
Widget。
CrossAxisAlignment.center
改为
CrossAxisAlignment.start
,并再次运行。
CrossAxisAlignment.start
改为其他值试试。
mainAxisAlignment
和
crossAxisAlignment
属性决定了
Row
和
Column
在各个轴上如何布局 widget。
Row
和
Column
首先布置固定大小的 widget。固定大小的小部件被认为是 不灵活的 因为它们布局后无法自我调整大小。
Flexible
widget 包裹一个 widget 让这个 widget 变得可以调整大小。当
Flexible
widget 包裹 widget 时,这个 Widget 就成为
Flexible
widget 的子节点,并被视为 flexible 的。在布置固定大小的 widget 后, Flex 的 widget 根据其
flex
和
fit
属性调整大小:
flex
flex
因子与其他的比较,以决定自身占剩余空间的比例。
fit
Flexible
的 Widget 是否能够填充所有剩余空间。
fit
属性,它可以使用这两个值之一:
FlexFit.loose
FlexFit.tight
fit
属性使
Flexible
widgets 能够填充剩余空间。
fit
的值设为
FlexFit.tight
,并再次运行。
Row
包含了一个
BlueBox
widget 和两个
Flexible
widgets 包裹的
BlueBox
Widget。
Flexible
widgets 包含了
flex
属性,并将其值设为 1。(默认值)
flex
属性互相比较时,它们的
flex
值的比率决定了
Flexible
Widget 自身所占剩余空间的比例。
remainingSpace * (flex / totalOfAllFlexValues)
flex
值的总和为(2),这决定了每个
Flexible
widgets 都能分到总剩余空间的一半空间。
BlueBox
widget(或是 fixed-size widget)得到了相同的大小。
flex
属性转换为其他值,例如 2 和 1。
Expanded
widget 能够包裹一个 Widget 并强制其填满剩余空间,与
Flexible
非常相似。
Flexible
在
Row
或
Column
中重新调整 widgets 的大小。这样,你就可以调整子 Widget 的间距同时保持其相对于父 Widget 的大小。
Expanded
改变子窗口小部件的约束,所以它会填补全部空白空间。
Expanded
widget
如何强制子窗口小部件填补额外空间。
BlueBox
widget 外包裹一个
Expanded
widget。
Expanded(child: BlueBox(),),
SizedBox
widget 的两种用途之一就是创建精确的尺寸。当
SizedBox
包裹了一个 Widget 时,它会使用
height
和
width
调整其大小。如果它没有包裹 Widget,它可以使用
height
和
width
属性创造空的空间。
SizedBox
widget 包裹了中间的
BlueBox
widget,并将
BlueBox
的宽度设为 100 逻辑像素。
SizedBox
widget 中的
height
设为 100 逻辑像素,并重新运行。
BlueBox
widget,其中第一个和第二个
BlueBox
widget 包裹
SizedBox。
SizedBox
widget 的
width
设为 50 逻辑像素。
BlueBox
widget 之间添加另一个 SizedBox widget (宽 25 逻辑像素)以创建更多空间。
SizedBox
相似,
Spacer
widget 也能在 Widgets 之间创建空间。
flex
属性创建一段空间,请使用
Spacer
。如果你想创建一个拥有特定逻辑像素值的空间,请使用
SizedBox
。
flex
值为 1 的
Spacer
widget,分隔最初的两个
BlueBox
widget。
BlueBox
widget 之间添加另一个
Spacer
widget。(flex 值仍然为 1)
Text
widget 不仅能够显示文字,并能够配置不同的字体,大小和颜色。
Row
的
crossAxisAlignment
和
textBaseline
属性。
CrossAxisAlignment.center
改为
CrossAxisAlignment.baseline
,
然后再次运行。
Icon
Widget 能够显示图形符号,这代表了 UI 的一个方面。Flutter 将会为 Material 和 Cupertino 的应用提前加载 icon packages。
Icons.widget
Widget。
Material Icon library
的
Icon
并将其大小设为 50。
Icon
何止一个来自
Material Color palette
的
Colors.amber
色,然后再次运行。
Image
widget 显示了一张图片。你还能够直接引用图片 URL,或是你的应用 package 中的图片。但是由于 DartPad 无法引用包图片,所以下面的样例将会使用网络上的图片。
Image.network
方法接收一个含有图片 url 的字符串。
Image.network
包含了一个短小的 URL。
https://github.com/flutter/website/blob/master/examples/layout/sizing/images/pic3.jpg?raw=true
pic3.jpg
改为
pic1.jpg
或
pic2.jpg
,然后重新运行。
Column
。然后你将会在
Column
包裹一个含有 icon 的
Row
,它将会被放在姓名和标题的左边。
Row
外包裹一个
Column
,所以你的代码中就包含了一个 Column(Row(Column))。然后你将调整最外面的
Column
的布局,所以它看起来不错。最后,您将添加联系信息到最外面的
Column
的 children 中,所以它将显示在名称,标题和图标下方。
Column
:
Text
widget 有一个叫做
Flutter McFlutter
的名字,并将其
style
属性设为
Theme.of(context).textTheme.headline
。
Text
widget 包含了标题
Experienced Dev
elope
r
。
Column
,将其
mainAxisSize
设为
MainAxisSize.min
,
crossAxisAlignment
设为
CrossAxisAlignment.start
。
Row
包裹一个
Column
:ßß
Icon
widget 设为
Icons.account_circle
,并设为 50 像素大小。
Icon
widget 外包裹一个
Padding
widget 以在其周围创建
match
8 像素的空间。为了完成这个,你可以指定其
match
padding
属性为
const EdgeInsets.all(8.0)
。
match
这个
Row
看上去会像这样:
Row(
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: Icon(Icons.account_circle, size: 50),
),
Column( ... ), // <--- The Column you first implemented
],
);
Column
包裹在
Row
外面,并将其
mainAxisSize
属性设为
MainAxisSize.min
,
crossAxisAlignment
属性设为
CrossAxisAlignment.stretch
。这个
Column
将包含以下 widgets:
SizedBox
widget。
Row
,你等会将用它来添加联系方式。
SizedBox
widget。
Row
,你将会添加四个图标。(第三部分)
Column
Widget 的列表格式应该如下一样,联系信息和图标显示在名称和头衔下方:
],
), // <--- Closing parenthesis for the Row
SizedBox(),
Row(), // First empty Row
SizedBox(),
Row(), // Second empty Row
],
); // <--- Closing parenthesis for the Column that wraps the Row
Row
中,添加两个
Text
widget。
Text
widget 包含了
123 Main Street
的地址。
Text
widget 包含了电话号
(415) 555-0198
。
Row
,将
mainAxisAlignment
属性设为
MainAxisAlignment.spaceBetween
。
Row
中添加以下四个
Icon
widget。
Icons.accessibility
Icons.timer
Icons.phone_android
Icons.phone_iphone
Row
,将其
mainAxisAlignment
属性设为
MainAxisAlignment.spaceAround
。