封面图 by Andrii Podilnyk on Unsplash
原文作者:Burhanuddin Rashid,Google 认证 Android 开发者
原文链接:Flutter For Android Developers : How to design LinearLayout in Flutter.
这篇博客是为那些想把现有的开发知识应用到 Flutter 的 Android 开发者写的。在这篇文章里,我们将会探索在 Flutter 中与 LinearLayout
对应的是什么。
博客系列
- 在 Flutter 中如何设计 Activity 界面
- 在 Flutter 中如何实现线性布局 (本篇)
- 在 Flutter 中如何设计 FrameLayout
先决条件
这篇文章假设你已经在电脑上配置好了 Flutter 的运行环境并且能成功运行一个 Hello World 项目。如果你还没有安装 Flutter,你可以从这里开始。
Dart
是一门基于面向对象的语言,对一个 Android 开发者来说是很容易掌握的。
让我们开始吧
如果你是一个 Android 开发者,我想你应该会在设计布局时重度使用LinearLayout
。对于不熟悉LinearLayout
的人,我先从官方定义开始吧。
线性布局就是一个把其他视图组件水平排成一行或垂直排成一列的布局。
有了上述定义和图片演示,你就可以知道在 Flutter 中与 LinearLayout 相同的部件。没错,它们就是Row
和Column
。这两个部件的行为几乎和 Android 原生的 LinearLayout 一模一样。Row 和 Column 在 Flutter 中也是被重度使用的部件。
注意:Row/Column 不会滚动。如果你有一些线性排列的控件并且希望在空间不够时它们可以滚动,可以考虑使用ListView。
现在我们将会讨论 LinearLayout 的一些主要属性,这些属性在 Flutter 中有所对应。
1.方向(Orientation)
在 LinearLayout 中你可以通过android:orientation=”horizontal”
这个属性来指定子控件的排列方向。这个属性接收horizontal/vertical
两个值,对应 Flutter 中的 Row/Column。
在 Android 中,LinearLayout 是一个ViewGroup
,它可以将其他的控件作为子控件。你可以在 <LinearLayout> </LinearLayout>
标签内声明它所有的子控件。
为了声明Row/Column
的子控件,我们需要用到 Row/Column
的children
属性,这个属性接收一个控件列表(List<Widget>
)。如下面代码所示:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text("LinearLayout Example"),
),
body: Container(
color: Colors.yellowAccent,
child: Row(
children: [
Icon(
Icons.access_time,
size: 50.0,
),
Icon(
Icons.pie_chart,
size: 100.0,
),
Icon(
Icons.email,
size: 50.0,
)
],
),
),
),
);
}
}
在这个例子中,我们使用了Row
这个控件,相当于设置了android:orientation=”horizontal”
的 LinearLayout。如果是垂直的情况我们就用Column
。
如果你想知道
Scaffold
在这里的用处,你可以阅读我之前的文章在 Flutter 中如何设计 Activity 界面?。
下图是上面代码的输出结果:
LinearLayout(属性) | 值 | Flutter 部件 |
---|---|---|
android:orientation | horizontal | Row |
android:orientation | vertical | Column |
2."match_parent" 和 "wrap_content"
MATCH_PARENT
:即控件想和父控件一样大,如果你的控件是顶级根控件,那么它将和屏幕一样大。WRAP_CONTENT
:即控件的大小刚好够包围它的内容。
为了获得与match_parent
和wrap_content
一样的行为,我们需要用到 Row和 Column 的mainAxisSize
属性,这个属性接收MainAxisSize
枚举。MainAxisSize
有两个值,MainAxisSize.min
即wrap_content
,MainAxisSize.max
即match_parent
。
在上面的例子中,我们并没有给Row
指定mainAxisSize
属性,所以它将默认设为MainAxisSize.max
,即match_parent
。黄色背景展示了容器的剩余空间是如何被覆盖的。
下面代码演示了在上面例子如何指定mainAxisSize
属性,不同值对应的输出结果如下图所示。
....
body: Container(
color: Colors.yellowAccent,
child: Row(
mainAxisSize: MainAxisSize.min,
children: [...],
),
)
...
这样我们就可以从视觉上区分 mainAxisSize
的不同值是如何应用在 Row 和 Column上的。
3.重力(Gravity)
重力指定了子控件如何在自身范围内定位其内容,我们在LinearLayout中使用android:gravity=”center”
属性指定重力,这个属性接收多个标识如何对齐的值。在Row和Column中,我们可以使用MainAxisAlignment
和CrossAxisAlignment
达到同样的效果。
1.MainAxisAlignment:
这个属性指定了子控件在主轴方向上如何被放置。为了让这个属性生效,在Row和Column中必须有剩余空间。如果你将mainAxisSize
属性设为MainAxisSize.min
,那么设置MainAxisAlignment
属性将会没有效果,因为没有剩余空间可用。
我们可以像下面这样指定MainAxisAlignment
属性:
....
body: Container(
color: Colors.yellowAccent,
child: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.start,
children: [...],
),
)
...
一图胜千言,与其用语言描述每个属性,不如用图片来展示更直观。
下面的输出比较了 LinearLayout
的属性和 Row
的MainAxisAlignment
属性。
然后我们来比较一下Column
部件:
练习:你可以试下其他的枚举:
spaceEvenly
,spaceAround
,spaceBetween
,它们与ConstraintLayout
中用到了水平/垂直链(chain)行为一致。
2.CrossAxisAlignment:
这个属性指定了子控件在交叉轴方向如何放置。即如果我们使用Row
部件,那么子控件的重力则基于垂直线;如果我们 使用Column
部件,那么子控件的重力则基于水平线。
这听起来有点让人费解,不过随着继续阅读你将会理解它。
为了便于理解 ,我们将mainAxisSize
设为MainAxisSize.min
。你可以像下面代码一样声明CrossAxisAlignment
属性,如果没有设置,那么默认值为CrossAxisAlignment. start
。
....
body: Container(
color: Colors.yellowAccent,
child: Row(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [...],
),
)
...
下图比较了LinearLayout 的属性和Row
的CrossAxisAlignment
属性:
下面再来看看Column
属性:
stretch
表现的有点不一样,它把控件拉伸以占满交叉轴剩余的空间(match_parent
)。
4.布局比重(Layout Weight))
为了创建一个子控件平分空间或按一定比例使用空间的线性布局,我们把每个控件的android:layout_height
设为0dp
(垂直方向)或者把每个控件的android:layout_width
设为0dp
(水平方向),然后把每个控件的android:layout_weight
设为 1 或者其他你想要分配的值。
在 Flutter 中,为了使用 Row和 Column 达到相同的效果,我们用一个Expanded
控件来包裹 Row/Column
的子控件。Expanded
控件有一个flex
属性,和android:layout_weight
作用是一样的,因此通过指定flex
的值我们就能指定子控件所占的空间比。
下面代码演示了如何定义控件的flex
值:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text("LinearLayout Example"),
),
body: Container(
color: Colors.yellowAccent,
child: Container(
child: Row(
children: [
Expanded(
child: Container(
child: Icon(
Icons.access_time,
size: 50.0,
),
color: Colors.red,
),
flex: 2,
),
Expanded(
child: Container(
child: Icon(
Icons.pie_chart,
size: 100.0,
),
color: Colors.blue,
),
flex: 4,
),
Expanded(
child: Container(
child: Icon(
Icons.email,
size: 50.0,
),
color: Colors.green,
),
flex: 6,
),
],
),
),
),
),
);
}
}
为了便于理解,我们将每个图标用一个带背景色的容器控件包裹起来,这样容易看出它们占用了多少空间:
总结
和 Android 中的 LinearLayout 一样,Row 和 Column 在 Flutter 中也是被重度使用的控件,希望在接下来的博客中讨论更多内容。
我创建了一个样例 App 来演示Row 和 Column 的 属性以及它们组合起来使用的效果。
你可以在这里看到它。
感谢阅读。