Codelab: Flutter 布局基础教程
请注意,本文档已在官方文档中被移除,新的页面正在翻译中,本页面作为临时替代页面,将在新页面翻译结束之后删除,请勿在站外引用或分享以避免不好的用户体验,谢谢!
Row
and Column
是 Flutter 世界非常重要的两个 widgets。想要把一个带 label 的 Text
widget 放到另一个具有相应值的 Text
widget边上?使用一个 Row
。想要现实多对 labels 和值?使用一个包含多个 Row
的 Column
。包含多个字段的表单,旁边有图标的菜单选项,旁边有搜索栏的按钮,这些都是要用到 Row
s 和 Column
的地方。
codelab 将带你了解 Row
s and Column
如何工作。因为它们非常相似,一旦你学会了如何使用 Row
,codelab 将会向你展示如何把相同的概念应用于 Column
。使用内置的编辑器,你将会边玩边测试你学到的知识。
从一个 Row 和一些 children开始
Row
or Column
的主要功能就是包含其他的 widgets,这些 widgets 被称为 children。在一个 Row
里,所有的 children 都会根据 text 方向从头到尾水平排列。如果你的设备被设置为英文或其他从左到右的语言,就会从左开始,如果你使用阿拉伯语或者其他从右到左显示的语言,就会从右到左排列。
代码例子
下面是一个叫作 MyWidget
的 widget,在其内部创建了一个 Row
,然后请试着将三个 BlueBox
widgets 加到 Row
的 children中。
主轴空间
Row
的主轴是指水平方向的轴(Column
的主轴是指竖直方向的)。每一个 Row
都有一个叫 mainAxisSize
的属性,它决定了此 Row
沿着水平方向占用空间的大小。默认情况下,mainAxisSize
的值为 MainAxisSize.max
,这意味着 Row
将会占用所有可用的水平方向空间。你可以使用 MainAxisSize.min
实现让一个 Row
占用尽可能少的空间。
代码例子
这里的例子是你刚刚完成的。试着将 Row
的 mainAxisSize
的值设为 MainAxisSize.min
,看看会发生什么。
主轴对齐
如果你将一个 Row
的 mainAxisSize
设为最小值,在 children 之外就不会有更多的空间。如果你将其设为最大值,Row
就会有多出来的空间。你可以使用 mainAxisAlignment
属性来控制
Row
中的 children 对齐的方式。
MainAxisAlignment
有六种不同的枚举值:
-
将所有的 children 尽可能向
Row
的 start 方向排列(如果是从左到右,那就是靠左排列)。 -
将所有的 children 尽可能向
Row
的 end 方向排列。 -
将 children 聚在
Row
主轴的中间位置。 -
将主轴空白位置进行均分,用来在 children 之间制造间隔,首尾 children 距边缘没有间隙。
-
很像
spaceBetween
,除了让首尾 children 距边缘也有相同的间隙。 -
很像
spaceEvenly
,只是首尾 children 距边缘间距为中间 children 间距的一半。
代码例子
下面的 row 的 mainAxisAlignment
被设为了 start。试着将其改为其他的值,然后重新运行看看会怎么样。
交叉轴对齐
Row
widgets 的交叉轴是竖直方向的轴,你可以用 crossAxisAlignment
属性来控制
children 如何在垂直方向排列。默认值是 CrossAxisAlignment.center
,一共有五种值:
-
将所有的 children 向
Row
竖直方向的 start 方向排列(如果是从上到下,你可以修改verticalDirection
来改变)。 -
将所有的 children 向
Row
竖直方向的 end 方向排列(默认是底部)。 -
将 children 聚在
Row
竖直方向轴的中间位置。 -
所有的 Children 的高度会被拉伸到和
Row
一样,填满竖直方向轴的空间。 -
所有的 Children 的 baselines 在竖直方向对齐。
代码例子
Row
有两个小的 children 和一个大的。crossAxisAlignment
属性默认为 center。可以试着将其变为其他值然后重新运行,看看会怎样。
会有一个警告: CrossAxisAlignment.baseline
requires
that another property be set as well, so you
will see an error if you try that one. 不用担心,在下一节将会对此进行讨论。
基线对齐
Sometimes it’s handy to align widgets containing text not by their
overall bounds, but by the baselines used by their characters.
That’s what CrossAxisAlignment.baseline
is for. You
can use it in combination with a Row
’s textBaseline
property (which indicates which baseline to use) to align a
Row
’s children along their baselines.
有时候根据包含文本的 widgets 的基线对齐是比较方便的,而不是根据它们的整体边框对齐。那就是 CrossAxisAlignment.baseline 的用途。你可以使用联合使用 Row
的 textBaseline
属性(决定按照哪种基线来对齐),来决定 Row
的所有 children 根据基线对齐。
代码例子
row 里包含三个拥有不同字体大小的 Text
widgets。试着将 crossAxisAlignment
属性设为 baseline
,然后试验 textBaseline
的不同值(TextBaseline
枚举值里包含可用的 baseline 值)。
可伸缩 children
到目前为止,例子中所有用作 children 的 widgets 都有一个固定的大小。不过 Row
可以让它的 children 可伸缩,来适应可用的空间。为了更好的理解这是怎么回事儿,最好看看 Row
的大小和它的 children。
-
首先,
Row
首先会要求它所有的 children 想要多大的尺寸。 -
然后,它会计算主轴(水平)的剩余空间。
-
然后它把剩下的空间根据 children 的 flex 值分给它的可伸缩的 children,这些可伸缩的 children 可以使用他们提供的部分或者全部的空间。
-
在那时,
Row
知道所有的 children 的尺寸有多大,然后可以根据你之前学到的 axis size 和 alignment 属性来排列它们。
大多数 widgets 是固定大小的。你可以将他们包裹在一个 Flexible
widget 中来将它们变为可伸缩的。
Flexibles
有两个重要属性: flex
值决定与其他 children 相比可占用剩余空间的多少, fit
属性决定其 child 是否占用所有额外的空间。
代码例子
试着将 row 中间的 box 包裹在一个 flex
factor 为 1 并且 fit
为 FlexFit.loose
的 Flexible widget中。然后试着将
fit 改为
FlexFit.tight`,看看会发生什么。
flex
factor 为 1 和 fit
为 FlexFit.tight
的组合是非常常见的, 更简单的方式是直接使用 Expanded
widget.
Flex factors
如果 Row
或 Column
中多个 children 都是可伸缩的,那么如何分配可用空间取决于它们的 flex
值。每个 child 获得的空间将取决于他们的 flex 值占所有 children 的 flex 值之和的比例。
remainingSpace * (flex / totalOfAllFlexValues)
例如,如果有两个 flex 值为 1 的 children,每个将获得一半的可用空间。如果有两个 flex 值为 1 的 children,还有一个 flex 值为 2 的 child,那么前两个 children 将各获得四分之一的可用空间,另一个 child 将获得一半的可用空间。
代码例子
在这个例子中, Row
的所有三个 children 都是可伸缩的,试着改变它们的 flex
值然后重新运行看看它们的尺寸如何改变。
如果没有空间了怎么办?
正如你所看到的,当一个 Row
问它其中一个可伸缩的 child 想要多大空间时,它会根据这个 child 的 flex
值分配给它一个最大值。但是固定大小的 children 没有这个限制,它们可以自己决定大小。
一个副作用就是,无法阻止一个固定大小的 child 声明超出 Row
所能支持的大小。当这种情况发生时,就会发生溢出。你可以通过修改这个 child 的大小或者使用一个可滚动的 widget 来解决这个问题。
代码例子
下面的 Row
包含一个特别宽的 widget。运行代码看会发生什么,然后试着修改Container
的宽度使其适应。
试着使用 SizedBox 来留出空间
如果你需要在一个 Row
中的两个 children 之间指定一个特定的间隔,一个简单的方法是在中间放一个宽度合适的 SizedBox
。
代码例子
试着用一个宽度 100 的 SizedBox
在两个 items 中间制造一些间隔。
Spacers 留出可变空间
使用Spacers
是另一个在 Row
的 children 之间留出空间的方法。它们是可伸缩的,可以填满任何剩下的空间。
代码例子
试着在第一个和第二个 children 之间加一个 Spacer
。
等等, 我不是还要学习 Columns 吗?
给你个惊喜,你已经学习了。Row
的所有用法和 Column
是一样的,只是维度不同。
Row
的主轴是水平的,而 Column
的主轴是竖直的,但是它们设置其 children 的大小和位置的方式是一样的。它们还共用一个基本类 Flex
。所以你已经学习的有关 Row
的用法,同样适用于 Column
。
代码例子
这里有一个包含不同尺寸和一些重要属性已经设置好的 children 的 Column
。试着摆弄以下,你会发现 Column
就像一个竖过来的的 Row
。
将它们放在一起
现在你已经熟悉了 Row
和 Column
的重要属性,你已经可以来联系将它门组合在一起来构建用户界面。下面的例子将带你完成一个名片显示的构建。
代码例子
每一张名片都需要一个名字和头衔,让我们从这里开始。
-
添加一个
Column
widget -
添加两个 text widgets 到
Column
的 children 列表中:-
第一个是名字(简短一点更适合于一个小窗口),使用
headline
样式:
-
style: Theme.of(context).textTheme.headline
-
第二个 text widget 应该是
Experienced App Developer
,使用默认样式(不用设置style
属性)。 -
设置
Column
的crossAxisAlignment
为 start,使得 text widgets 会开始对齐,而不是居中。 -
将
Column
的mainAxisSize
设为MainAxisSize.min
,这样 card 才不会扩展到整个 window 那么高。
名片的左上角通常会有一个图标或者标志,所以下一步是加一个到你的名片上。将你刚创建的 Column
包裹在一个 Row
widget 中。
Row(
children: [
Column( … ), // <- This should be the Column you made in the previous step
],
);
现在你可以添加一个图标:
-
在你的
Row
的Column
的前面,加一个Padding
widget。-
设置
padding
为const EdgeInsets.all(8)
。 -
将一个
Icon
widget 作为Padding
widget 的 child。-
你可以使用任何 icon resource,
Icons.account_circle
看起来就不错。 -
把
Icon
的大小设置为 50。
-
-
你的第一个 Row
现在完成了。还有两件事要做,你需要一个 Column
把它们放进去。把你的 Row
包裹进一个 Column
widget 就像这样:
Column(
children: [
Row( … ), // <- This should be the Row with your Icon and Text widgets.
],
);
然后按照以下步骤完成你的新 Column
:
-
设置
Column
的 mainAxisSize 为最小-
否则它会充满整个屏幕!
-
-
设置
Column
的crossAxisAlignment
为 stretch。-
这使得所有 children 都会拉伸到最大宽度
-
-
在
Column
中你的Row
下面添加更多的 widgets:-
一个高度为 8 的
SizedBox
-
一个空
Row
(没有 children 或其他属性) -
一个高度为 16 的
SizedBox
-
另一个空
Row
-
现在就差几步了。接下来是第二个 Row
。添加以下的 widgets 作为它的 children:
-
一个地址为 ‘123 Main Street’ 的
Text
widget -
一个电话为 ‘800-123-1234’ 的
Text
widget
如果你现在运行代码,你会看到这两个 Text
widgets 是挨着的,而不是在 Row
的两端对齐,这是不对的。你可以将 Row
的 mainAxisAlignment
设为 spaceBetween
,使得这两个 Text
widge 中间有些间隔。
最后一步是在名片的底部放一些图标。
-
添加四个
Icon
widgets 到最后一个Row
中。你可以使用任何你喜欢的图标资源,但是以下图标是一个很好的选择,用来展示你想象中的关注于 accessibility, fast development, and multi-platform apps 的开发人员:Icons.accessibility
Icons.timer
Icons.phone_android
Icons.phone_iphone
-
设置
Row
的mainAxisAlignment
属性为MainAxisAlignment.spaceAround
。