Flutter 布局基础教程

欢迎来到 Flutter 布局 codelab!你将在这里学到如何构建 Flutter UI,更棒的是这一切都不需要安装 Flutter 或者 Dart!

Welcome to the Flutter layout codelab, where you learn how to build a Flutter UI without downloading and installing Flutter or Dart!

Flutter 与其他框架有着明显的差异,原因在于它使用代码来构建 UI,而不是 XML 或其他东西。其中,Widget 是构建 Flutter UI 的基本单元。当你逐渐深入这个 codelab,你将会发现在 Flutter 中几乎所有的东西都是 Widget。 Widget 是一个不会改变的对象,它是 UI 中一个特定部分的描述。你还会学到 Flutter 的 Widget 非常容易组合,这意味着你能够通过组合已有的 Widgets 来创造更多复杂的 Widgets。到这篇文章的最后,你会运用这里所学的知识构建一个显示名片的 Flutter UI。

Flutter is different from other frameworks because its UI is built in code, not (for example) in an XML file or similar. Widgets are the basic building blocks of a Flutter UI. As you progress through this codelab, you’ll learn that almost everything in Flutter is a widget. A widget is an immutable object that describes a specific part of a UI. You’ll also learn that Flutter widgets are composable, meaning, that you can combine existing widgets to make more sophisticated widgets. At the end of this codelab, you’ll get to apply what you’ve learned into building a Flutter UI that displays a business card.

本 codelab 的预期完成时间约为 45 - 60 分钟

Estimated time to complete this codelab: 45-60 minutes.

Row 和 Column 类

Row and Column classes

RowColumn 是两个用来容纳和布局 Widgets 的类。在它们内部的 Widgets 我们称为 childrenRowColumn 就作为它们的父级。 Row 将会让 Widgets 水平排列,而 Column 则会让其竖直排列。

Row and Column are classes that contain and lay out widgets. Widgets inside of a Row or Column are called children, and Row and Column are referred to as parents. Row lays out its widgets horizontally, and Column lays out its widgets vertically.

样例:创建一个 Column

Example: Creating a Column

轴大小和对齐方式

Axis size and alignment

至此,BlueBox widget 已经在一起被压扁了(在界面的左边或者上面)。你可以通过轴大小和对齐属性来改变 BlueBox Widget 的间距。

So far, the BlueBox widgets have been squished together (either to the left or at the top of the UI Output). You can change how the BlueBox widgets are spaced out using the axis size and alignment properties.

mainAxisSize 属性

mainAxisSize property

RowColumn分别占据了不同的主轴。Row 的主轴是水平的。 mainAxisSize 决定了 RowColumn 能够在主轴上占据多大空间。 mainAxisSize 有两个可选属性:

Row and Column occupy different main axes. A Row’s main axis is horizontal, and a Column’s main axis is vertical. The mainAxisSize property determines how much space a Row and Column can occupy on their main axes. The mainAxisSize property has two possible values:

MainAxisSize.max
RowColumn 占据它们主轴上所有空间。如果子 Widget 的总宽度小于主轴上的空间,它们就会充满剩余的空间。

MainAxisSize.max
Row and Column occupy all of the space on their main axes. If the combined width of their children is less than the total space on their main axes, their children are laid out with extra space.

MainAxisSize.min
RowColumn 仅占据它的 children 在主轴上所需的空间,它的 children 在主轴之间将没有额外空间。

MainAxisSize.min
Row and Column only occupy enough space on their main axes for their children. Their children are laid out without extra space and at the middle of their main axes.

样例:自定义轴大小

Example: Modifying axis size

mainAxisAlignment 属性

mainAxisAlignment property

mainAxisSize 被设为 MainAxisSize.max, RowColumn 将会使用额外空间来对齐它的 children。 mainAxisAlignment 属性决定了 RowColumn 将会在额外空间中如何对齐它的 children。 mainAxisAlignment 有以下六个可选属性:

When mainAxisSize is set to MainAxisSize.max, Row and Column might lay out their children with extra space. The mainAxisAlignment property determines how Row and Column can position their children in that extra space. mainAxisAlignment has six possible values:

MainAxisAlignment.start
将其 children 从主轴起点处开始对齐。(Row 的起点在左边,Column 的起点在顶部)

MainAxisAlignment.start
Positions children near the beginning of the main axis. (Left for Row, top for Column)

MainAxisAlignment.end
将其 children 从主轴终点处开始对齐。(Row 的终点在右边,Column 的终点在底部)

MainAxisAlignment.end
Positions children near the end of the main axis. (Right for Row, bottom for Column)

MainAxisAlignment.center
将其 children 置于主轴中心。

MainAxisAlignment.center
Positions children at the middle of the main axis.

MainAxisAlignment.spaceBetween
在 children 之间平均分配额外空间。

MainAxisAlignment.spaceBetween
Divides the extra space evenly between children.

MainAxisAlignment.spaceEvenly
在 children 之间,以及第一个 children 之前和最后一个 children 之后,平均分配额外空间。

MainAxisAlignment.spaceEvenly
Divides the extra space evenly between children and before and after the children.

MainAxisAlignment.spaceAround
MainAxisAlignment.spaceEvenly 相似,但在第一个 child 之前以及最后一个孩子之后减少了一半的空间,让其 children 之间宽度缩减一半。

MainAxisAlignment.spaceAround
Similar to MainAxisAlignment.spaceEvenly, but reduces half of the space before the first child and after the last child to half of the width between the children.

样例:自定义主轴对齐方式

Example: Modifying main axis alignment

crossAxisAlignment 属性

crossAxisAlignment property

crossAxisAlignment 属性决定了 RowColumn 能够如何在其横轴上定位 children。 Row 的横轴是竖直的,而 Column 则是水平的, crossAxisAlignment 属性有五个可选属性:

The crossAxisAlignment property determines how Row and Column can position their children on their cross axes. A Row’s cross axis is vertical, and a Column’s cross axis is horizontal. The crossAxisAlignment property has five possible values:

CrossAxisAlignment.start
将其 children 横轴顶部对齐。(顶部是 Row,左侧是 Column

CrossAxisAlignment.start
Positions children near the start of the cross axis. (Top for Row, Left for Column)

CrossAxisAlignment.end
将其 children 横轴底部对齐。(底部是 Row,右侧是 Column

CrossAxisAlignment.end
Positions children near the end of the cross axis. (Bottom for Row, Right for Column)

CrossAxisAlignment.center
将其 children 横轴中心对齐。(中间是 Row,中心是 Column

CrossAxisAlignment.center
Positions children at the middle of the cross axis. (Middle for Row, Center for Column)

CrossAxisAlignment.stretch
沿横轴延伸 children。(在 Row 中是从顶至底,Column 则是从左至右)

CrossAxisAlignment.stretch
Stretches children across the cross axis. (Top-to-bottom for Row, left-to-right for Column)

CrossAxisAlignment.baseline
根据 children 的基线对子节点。(仅限Text类,并要求 textBaseline 属性设置为 TextBaseline.alphabetic。在 Text class 小节中查看样例。

CrossAxisAlignment.baseline
Aligns children by their character baselines. (Text class only, and requires that the textBaseline property is set to TextBaseline.alphabetic. See the Text widget section for an example.)

样例:自定义横轴对齐方式

Example: Modifying cross axis alignment

Flexible widget

正如你所看到,mainAxisAlignmentcrossAxisAlignment 属性决定了 RowColumn在各个轴上如何布局 widget。 RowColumn 首先布置固定大小的 widget。固定大小的小部件被认为是 不灵活的 因为它们布局后无法自我调整大小。

As you’ve seen, the mainAxisAlignment and crossAxisAlignment properties determine how Row and Column position widgets along both axes. Row and Column first lay out widgets of a fixed size. Fixed size widgets are considered inflexible because they can’t resize themselves after they’ve been laid out.

Flexible widget 包裹一个 widget 让这个 widget 变得可以调整大小。当 Flexible widget 包裹 widget 时,这个 Widget 就成为 Flexible widget 的子节点,并被视为 flexible 的。在布置固定大小的 widget 后, Flex 的 widget 根据其 flexfit 属性调整大小:

The Flexible widget wraps a widget, so the widget becomes resizable. When the Flexible widget wraps a widget, the widget becomes the Flexible widget’s child and is considered flexible. After inflexible widgets are laid out, the widgets are resized according to their flex and fit properties.:

flex
将自身的 flex 因子与其他的比较,以决定自身占剩余空间的比例。

flex
Compares itself against other flex properties before determining what fraction of the total remaining space each Flexible widget receives.

fit
决定 Flexible 的 Widget 是否能够填充所有剩余空间。

fit
Determines whether a Flexible widget fills all of its extra space.

样例:改变 fit 属性

Example: Changing fit properties

样例:测试 flex 值

Example: Testing flex values

Expanded widget

Expanded widget 能够包裹一个 Widget 并强制其填满剩余空间,与 Flexible 非常相似。

Similar to Flexible, the Expanded widget can wrap a widget and force the widget to fill extra space.

样例:填补额外空间

Example: Filling extra space

SizedBox widget

SizedBox widget 的两种用途之一就是创建精确的尺寸。当 SizedBox 包裹了一个 Widget 时,它会使用 heightwidth 调整其大小。如果它没有包裹 Widget,它可以使用heightwidth属性创造空的空间。

The SizedBox widget can be used in one of two ways when creating exact dimensions. When SizedBox wraps a widget, it resizes the widget using the height and width properties. When it doesn’t wrap a widget, it uses the height and width properties to create empty space.

样例:调整一个 Widget

Example: Resizing a widget

样例:创建空间

Example: Creating space

Spacer widget

SizedBox 相似,Spacer widget 也能在 Widgets 之间创建空间。

Similar to SizedBox, the Spacer widget also can create space between widgets.

Example:创建更多空间

Example: Creating more space

Text widget

Text widget 不仅能够显示文字,并能够配置不同的字体,大小和颜色。

The Text widget displays text and can be configured for different fonts, sizes, and colors.

样例:文字对齐

Example: Aligning text

Icon widget

Icon Widget 能够显示图形符号,这代表了 UI 的一个方面。 Flutter 将会为 MaterialCupertino 的应用提前加载 icon packages。

The Icon widget displays a graphical symbol that represents an aspect of the UI. Flutter is preloaded with icon packages for Material and Cupertino applications.

样例:创建一个 Icon

Example: Creating an Icon

Image widget

Image widget 显示了一张图片。你还能够直接引用图片 URL,或是你的应用 package 中的图片。但是由于 DartPad 无法引用包图片,所以下面的样例将会使用网络上的图片。

The Image widget displays an image. You either can reference images using a URL, or you can include images inside your app package. Since DartPad can’t package an image, the following example uses an image from the network.

样例:显示一张图片

Example: Displaying an image

综合练习

Putting it all together

你就要完成这个 codelab 了!如果你想要检验你刚学的知识,为何不讲这些结合起来,构建一个显示名片的 Flutter UI 呢!

You’re almost at the end of this codelab. If you’d like to test your knowledge of the techniques that you’ve learned, why not apply those skills into building a Flutter UI that displays a business card!

Completed business card

你将会把 Flutter 的布局分解成几个部分,这就是如何在实际开发中构建 Flutter UI 方式!

You’ll break down Flutter’s layout into parts, which is how you’d create a Flutter UI in the real world.

第一部分, 你将会实现包含姓名和标题的 Column。然后你将会在 Column 包裹一个含有 icon 的 Row,它将会被放在姓名和标题的左边。

In Part 1, you’ll implement a Column that contains the name and title. Then you’ll wrap the Column in a Row that contains the icon, which is positioned to the left of the name and title.

Completed business card

第二部分中,你将会在 Row 外包裹一个 Column,所以你的代码中就包含了一个 Column(Row(Column))。然后你将调整最外面的Column的布局,所以它看起来不错。最后,您将添加联系信息到最外面的Column的 children 中,所以它将显示在名称,标题和图标下方。

In Part 2, you’ll wrap the Row in a Column, so the code contains a Column within a Row within a Column. Then you’ll tweak the outermost Column’s layout, so it looks nice. Finally, you’ll add the contact information to the outermost Column’s list of children, so it’s displayed below the name, title, and icon.

Completed business card

第三部分,你将会完成添加了更多图标的名片,它们会被放在联系信息的下方。

In Part 3, you’ll finish building the business card display by adding four more icons, which are positioned below the contact information.

Completed business card

第一部分

Part 1

练习:创建 name 和 title

Exercise: Create the name and title

练习:在 Column 外包裹一个 Row

Exercise: Wrap the Column in a Row

第二部分

Part 2

练习:调整布局

Exercise: Tweak the layout

练习:输入联系信息

Exercise: Enter contact information

第三部分

Part 3

练习:添加四个图标

Exercise: Add four icons

下一步是什么?

What’s next?

恭喜你,已经完成了这个 codelab!如果你想要了解关于 Flutter 的更多信息,这里有些值得探索的资源要推荐给你:

Congratulations, you’ve finished this codelab! If you’d like to know more about Flutter, here are a few suggestions for resources worth exploring:

你可以在 install 页面中下载 Flutter。

You can download Flutter from the install page.