[译]在 Flutter 中如何实现线性布局?

1,656 阅读6分钟

封面图 by Andrii Podilnyk on Unsplash

原文作者:Burhanuddin Rashid,Google 认证 Android 开发者

原文链接:Flutter For Android Developers : How to design LinearLayout in Flutter.

这篇博客是为那些想把现有的开发知识应用到 Flutter 的 Android 开发者写的。在这篇文章里,我们将会探索在 Flutter 中与 LinearLayout 对应的是什么。

博客系列

先决条件

这篇文章假设你已经在电脑上配置好了 Flutter 的运行环境并且能成功运行一个 Hello World 项目。如果你还没有安装 Flutter,你可以从这里开始

Dart 是一门基于面向对象的语言,对一个 Android 开发者来说是很容易掌握的。

让我们开始吧

如果你是一个 Android 开发者,我想你应该会在设计布局时重度使用LinearLayout。对于不熟悉LinearLayout的人,我先从官方定义开始吧。

线性布局就是一个把其他视图组件水平排成一行或垂直排成一列的布局。

线性布局

有了上述定义和图片演示,你就可以知道在 Flutter 中与 LinearLayout 相同的部件。没错,它们就是RowColumn。这两个部件的行为几乎和 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/Columnchildren属性,这个属性接收一个控件列表(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_parentwrap_content一样的行为,我们需要用到 Row和 Column 的mainAxisSize属性,这个属性接收MainAxisSize枚举。MainAxisSize有两个值,MainAxisSize.minwrap_content,MainAxisSize.maxmatch_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中,我们可以使用MainAxisAlignmentCrossAxisAlignment达到同样的效果。

1.MainAxisAlignment:

这个属性指定了子控件在主轴方向上如何被放置。为了让这个属性生效,在Row和Column中必须有剩余空间。如果你将mainAxisSize属性设为MainAxisSize.min,那么设置MainAxisAlignment属性将会没有效果,因为没有剩余空间可用。

我们可以像下面这样指定MainAxisAlignment属性:

....
body: Container(
  color: Colors.yellowAccent,
  child: Row(
    mainAxisSize: MainAxisSize.max,
    mainAxisAlignment: MainAxisAlignment.start,
    children: [...],
  ),
)
...

一图胜千言,与其用语言描述每个属性,不如用图片来展示更直观。

下面的输出比较了 LinearLayout 的属性和 RowMainAxisAlignment属性。

然后我们来比较一下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 的属性和RowCrossAxisAlignment属性:

下面再来看看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 的 属性以及它们组合起来使用的效果。

你可以在这里看到它。

感谢阅读。