Flutter 中的布局

Flutter 布局的核心机制是 widgets。在 Flutter 中,几乎所有东西都是 widget —— 甚至布局模型都是 widgets。你在 Flutter 应用程序中看到的图像,图标和文本都是 widgets。此外不能直接看到的也是 widgets,例如用来排列、限制和对齐可见 widgets 的行、列和网格。

The core of Flutter’s layout mechanism is widgets. In Flutter, almost everything is a widget—even layout models are widgets. The images, icons, and text that you see in a Flutter app are all widgets. But things you don’t see are also widgets, such as the rows, columns, and grids that arrange, constrain, and align the visible widgets.

你可以通过组合 widgets 来构建更复杂的 widgets 来创建布局。比如,下面第一个截图上有 3 个图标,每个图标下面都有一个标签:

You create a layout by composing widgets to build more complex widgets. For example, the first screenshot below shows 3 icons with a label under each one:

Sample layout Sample layout with visual debugging

第二个截图显示了可视布局,可以看到有一排三列,其中每列包含一个图标和一个标签。

The second screenshot displays the visual layout, showing a row of 3 columns where each column contains an icon and a label.

以下是这个 UI 的 widget 树形图:

Here’s a diagram of the widget tree for this UI:

Node tree

图上大部分应该和你预想的一样,但你可能会疑惑 containers(图上粉色显示的)是什么。Container 是一个 widget,允许你自定义其子 widget。举几个例子,如果要添加 padding、margin、边框或背景颜色,你就可以用上 Container 了。

Most of this should look as you might expect, but you might be wondering about the containers (shown in pink). Container is a widget class that allows you to customize its child widget. Use a Container when you want to add padding, margins, borders, or background color, to name some of its capabilities.

在这个例子中,每个 Text widget 都被放在一个 Container 以添加 padding。整个 Row 也被放在一个 Container 中,以便添加 padding。

In this example, each Text widget is placed in a Container to add margins. The entire Row is also placed in a Container to add padding around the row.

这个例子其余部分的 UI 由属性控制。通过 Iconcolor 属性来设置它的颜色,通过 Text.style 属性来设置文字的字体、颜色、字重等等。列和行有一些属性可以让你指定子项垂直或水平的对齐方式以及子项应占用的空间大小。

The rest of the UI in this example is controlled by properties. Set an Icon’s color using its color property. Use the Text.style property to set the font, its color, weight, and so on. Columns and rows have properties that allow you to specify how their children are aligned vertically or horizontally, and how much space the children should occupy.

布局 widget

Lay out a widget

如何在 Flutter 中布局单个 widget?本节将介绍如何创建和显示单个 widget。本节还包括一个简单的 Hello World app 的完整代码。

How do you layout a single widget in Flutter? This section shows you how to create and display a simple widget. It also shows the entire code for a simple Hello World app.

在 Flutter 中,只需几步就可以在屏幕上显示文本、图标或图像。

In Flutter, it takes only a few steps to put text, an icon, or an image on the screen.

1. 选择一个布局 widget

1. Select a layout widget

根据你想要对齐或限制可见 widget 的方式从各种 layout widgets 中进行选择,因为这些特性通常会传递它所给包含的 widget。

Choose from a variety of layout widgets based on how you want to align or constrain the visible widget, as these characteristics are typically passed on to the contained widget.

本例使用将其内容水平和垂直居中的 Center

This example uses Center which centers its content horizontally and vertically.

2. 创建一个可见 widget

2. Create a visible widget

举个例子,创建一个 Text widget:

For example, create a Text widget:

Text('Hello World'),

创建一个 Image widget:

Create an Image widget:

Image.asset(
  'images/lake.jpg',
  fit: BoxFit.cover,
),

创建一个 Icon widget:

Create an Icon widget:

Icon(
  Icons.star,
  color: Colors.red[500],
),

3. 将可见 widget 添加到布局 widget

3. Add the visible widget to the layout widget

所有布局 widgets 都具有以下任一项:

All layout widgets have either of the following:

  • 一个 child 属性,如果它们只包含一个子项 —— 例如 CenterContainer

    A child property if they take a single child – for example, Center or Container

  • 一个 children 属性,如果它们包含多个子项 —— 例如 RowColumnListViewStack

    A children property if they take a list of widgets – for example, Row, Column, ListView, or Stack.

Text widget 添加进 Center widget:

Add the Text widget to the Center widget:

Center(
  child: Text('Hello World'),
),

4. 将布局 widget 添加到页面

4. Add the layout widget to the page

一个 Flutter app 本身就是一个 widget,大多数 widgets 都有一个 build() 方法,在 app 的 build() 方法中实例化和返回一个 widget 会让它显示出来。

A Flutter app is itself a widget, and most widgets have a build() method. Instantiating and returning a widget in the app’s build() method displays the widget.

对于 Material app,你可以使用 Scaffold widget,它提供默认的 banner、背景颜色,还有用于添加抽屉、提示条和底部列表弹窗的 API。你可以将 Center widget 直接添加到主页 body 的属性中。

For a Material app, you can use a Scaffold widget; it provides a default banner, background color, and has API for adding drawers, snack bars, and bottom sheets. Then you can add the Center widget directly to the body property for the home page.

lib/main.dart (MyApp)
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter layout demo',
      home: Scaffold(
        appBar: AppBar(
          title: Text('Flutter layout demo'),
        ),
        body: Center(
          child: Text('Hello World'),
        ),
      ),
    );
  }
}

非 Material apps

Non-Material apps

对于非 Material app,你可以将 Center widget 添加到 app 的 build() 方法里:

For a non-Material app, you can add the Center widget to the app’s build() method:

lib/main.dart (MyApp)
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Container(
      decoration: BoxDecoration(color: Colors.white),
      child: Center(
        child: Text(
          'Hello World',
          textDirection: TextDirection.ltr,
          style: TextStyle(
            fontSize: 32,
            color: Colors.black87,
          ),
        ),
      ),
    );
  }
}

默认情况下,非 Material app 不包含 AppBar、标题和背景颜色。如果你希望在非 Material app 中使用这些功能,则必须自己构建它们。以上 app 将背景颜色更改为白色,将文本更改为深灰色来模拟一个 Material app。

By default a non-Material app doesn’t include an AppBar, title, or background color. If you want these features in a non-Material app, you have to build them yourself. This app changes the background color to white and the text to dark grey to mimic a Material app.

完成! 启动这个 app,你应该能看到 Hello World

That’s it! When you run the app, you should see Hello World.

App 源码:

App source code:

Hello World

横向或纵向布局多个 widgets

Lay out multiple widgets vertically and horizontally

最常见的布局模式之一是垂直或水平 widgets。你可以使用 Row widget 水平排列 widgets,使用 Column widget 垂直排列 widgets。

One of the most common layout patterns is to arrange widgets vertically or horizontally. You can use a Row widget to arrange widgets horizontally, and a Column widget to arrange widgets vertically.

要在 Flutter 中创建行或列,可以将子 widgets 列表添加到 RowColumn widget 中。反过来,每个子项本身可以是一行或一列,依此类推。以下示例演示了如何在行或列中嵌套行或列。

To create a row or column in Flutter, you add a list of children widgets to a Row or Column widget. In turn, each child can itself be a row or column, and so on. The following example shows how it is possible to nest rows or columns inside of rows or columns.

这个布局被组织为 Row。这一行包含两个子项:左侧的列和右侧的图像:

This layout is organized as a Row. The row contains two children: a column on the left, and an image on the right:

Screenshot with callouts showing the row containing two children

左侧列的 widget 树嵌套着行和列。

The left column’s widget tree nests rows and columns.

Diagram showing a left column broken down to its sub-rows and sub-columns

你将在 嵌套行和列 中实现蛋糕介绍示例的一些布局代码。

You’ll implement some of Pavlova’s layout code in Nesting rows and columns.

对齐 widgets

Aligning widgets

你可以使用 mainAxisAlignmentcrossAxisAlignment 属性控制行或列如何对齐其子项。对于一行来说,主轴水平延伸,交叉轴垂直延伸。对于一列来说,主轴垂直延伸,交叉轴水平延伸。

You control how a row or column aligns its children using the mainAxisAlignment and crossAxisAlignment properties. For a row, the main axis runs horizontally and the cross axis runs vertically. For a column, the main axis runs vertically and the cross axis runs horizontally.

Diagram showing the main axis and cross axis for a row Diagram showing the main axis and cross axis for a column

MainAxisAlignmentCrossAxisAlignment 类为控制对齐提供了各种常量。

The MainAxisAlignment and CrossAxisAlignment classes offer a variety of constants for controlling alignment.

在以下示例中,3 个图像每个都是是 100 像素宽。渲染框(在本例中是整个屏幕)宽度超过 300 像素,因此设置主轴对齐方式为 spaceEvenly 会将空余空间在每个图像之间、之前和之后均匀地划分。

In the following example, each of the 3 images is 100 pixels wide. The render box (in this case, the entire screen) is more than 300 pixels wide, so setting the main axis alignment to spaceEvenly divides the free horizontal space evenly between, before, and after each image.

Row(
  mainAxisAlignment: MainAxisAlignment.spaceEvenly,
  children: [
    Image.asset('images/pic1.jpg'),
    Image.asset('images/pic2.jpg'),
    Image.asset('images/pic3.jpg'),
  ],
);

Row with 3 evenly spaced images

App 源码: row_column

App source: row_column

列的工作方式与行的工作方式相同。以下示例展示了包含 3 个图像的列,每个图像的高度为 100 像素。渲染框(在本例中是整个屏幕)高度超过 300 像素,因此设置主轴对齐方式为 spaceEvenly 会将空余空间在每个图像之间、之上和之下均匀地划分。

Columns work the same way as rows. The following example shows a column of 3 images, each is 100 pixels high. The height of the render box (in this case, the entire screen) is more than 300 pixels, so setting the main axis alignment to spaceEvenly divides the free vertical space evenly between, above, and below each image.

Column(
  mainAxisAlignment: MainAxisAlignment.spaceEvenly,
  children: [
    Image.asset('images/pic1.jpg'),
    Image.asset('images/pic2.jpg'),
    Image.asset('images/pic3.jpg'),
  ],
);

App 源码: row_column

App source: row_column

Column showing 3 images spaced evenly

调整 widgets 大小

Sizing widgets

当某个布局太大而超出屏幕时,受影响的边缘会出现黄色和黑色条纹图案。这里有一个行太宽的 例子

When a layout is too large to fit a device, a yellow and black striped pattern appears along the affected edge. Here is an example of a row that is too wide:

Overly-wide row

通过使用 Expanded widget,可以调整 widgets 的大小以适合行或列。要修复上一个图像行对其渲染框来说太宽的示例,可以用 Expanded widget 把每个图像包起来。

Widgets can be sized to fit within a row or column by using the Expanded widget. To fix the previous example where the row of images is too wide for its render box, wrap each image with an Expanded widget.

Row(
  crossAxisAlignment: CrossAxisAlignment.center,
  children: [
    Expanded(
      child: Image.asset('images/pic1.jpg'),
    ),
    Expanded(
      child: Image.asset('images/pic2.jpg'),
    ),
    Expanded(
      child: Image.asset('images/pic3.jpg'),
    ),
  ],
);

Row of 3 images that are too wide, but each is constrained to take only 1/3 of the space

App 源码: sizing

App source: sizing

也许你想要一个 widget 占用的空间是兄弟项的两倍。为了达到这个效果,可以使用 Expanded widget 的 flex 属性,这是一个用来确定 widget 的弹性系数的整数。默认的弹性系数为 1。以下代码将中间图像的弹性系数设置为 2:

Perhaps you want a widget to occupy twice as much space as its siblings. For this, use the Expanded widget flex property, an integer that determines the flex factor for a widget. The default flex factor is 1. The following code sets the flex factor of the middle image to 2:

Row(
  crossAxisAlignment: CrossAxisAlignment.center,
  children: [
    Expanded(
      child: Image.asset('images/pic1.jpg'),
    ),
    Expanded(
      flex: 2,
      child: Image.asset('images/pic2.jpg'),
    ),
    Expanded(
      child: Image.asset('images/pic3.jpg'),
    ),
  ],
);

Row of 3 images with the middle image twice as wide as the others

App 源码: sizing

App source: sizing

组合 widgets

Packing widgets

默认情况下,行或列沿其主轴会占用尽可能多的空间,但如果要将子项紧密组合在一起,请将其 mainAxisSize 设置为 MainAxisSize.min。以下示例使用此属性将星形图标组合在一起。

By default, a row or column occupies as much space along its main axis as possible, but if you want to pack the children closely together, set its mainAxisSize to MainAxisSize.min. The following example uses this property to pack the star icons together.

Row(
  mainAxisSize: MainAxisSize.min,
  children: [
    Icon(Icons.star, color: Colors.green[500]),
    Icon(Icons.star, color: Colors.green[500]),
    Icon(Icons.star, color: Colors.green[500]),
    Icon(Icons.star, color: Colors.black),
    Icon(Icons.star, color: Colors.black),
  ],
)

Row of 5 stars, packed together in the middle of the row

App 源码: pavlova

App source: pavlova

嵌套行和列

Nesting rows and columns

布局框架允许你根据需要在行和列内嵌套行和列。让我们看看以下布局的概述部分的代码:

The layout framework allows you to nest rows and columns inside of rows and columns as deeply as you need. Let’s look the code for the outlined section of the following layout:

Screenshot of the pavlova app, with the ratings and icon rows outlined in red

概述的部分实现为两行,评级一行包含五颗星和评论的数量,图标一行包含由图标与文本组成的三列。

The outlined section is implemented as two rows. The ratings row contains five stars and the number of reviews. The icons row contains three columns of icons and text.

以下是评级行的 widget 树形图:

The widget tree for the ratings row:

Ratings row widget tree

ratings 变量创建了一个行,其中包含较小的由 5 个星形图标和文本组成的一行:

The ratings variable creates a row containing a smaller row of 5 star icons, and text:

var stars = Row(
  mainAxisSize: MainAxisSize.min,
  children: [
    Icon(Icons.star, color: Colors.green[500]),
    Icon(Icons.star, color: Colors.green[500]),
    Icon(Icons.star, color: Colors.green[500]),
    Icon(Icons.star, color: Colors.black),
    Icon(Icons.star, color: Colors.black),
  ],
);

final ratings = Container(
  padding: EdgeInsets.all(20),
  child: Row(
    mainAxisAlignment: MainAxisAlignment.spaceEvenly,
    children: [
      stars,
      Text(
        '170 Reviews',
        style: TextStyle(
          color: Colors.black,
          fontWeight: FontWeight.w800,
          fontFamily: 'Roboto',
          letterSpacing: 0.5,
          fontSize: 20,
        ),
      ),
    ],
  ),
);

评级行下方的图标行包含 3 列,每列包含一个图标和两行文本,你可以在其 widget 树中看到:

The icons row, below the ratings row, contains 3 columns; each column contains an icon and two lines of text, as you can see in its widget tree:

Icon widget tree

iconList 变量定义了图标行:

The iconList variable defines the icons row:

final descTextStyle = TextStyle(
  color: Colors.black,
  fontWeight: FontWeight.w800,
  fontFamily: 'Roboto',
  letterSpacing: 0.5,
  fontSize: 18,
  height: 2,
);

// DefaultTextStyle.merge() allows you to create a default text
// style that is inherited by its child and all subsequent children.
final iconList = DefaultTextStyle.merge(
  style: descTextStyle,
  child: Container(
    padding: EdgeInsets.all(20),
    child: Row(
      mainAxisAlignment: MainAxisAlignment.spaceEvenly,
      children: [
        Column(
          children: [
            Icon(Icons.kitchen, color: Colors.green[500]),
            Text('PREP:'),
            Text('25 min'),
          ],
        ),
        Column(
          children: [
            Icon(Icons.timer, color: Colors.green[500]),
            Text('COOK:'),
            Text('1 hr'),
          ],
        ),
        Column(
          children: [
            Icon(Icons.restaurant, color: Colors.green[500]),
            Text('FEEDS:'),
            Text('4-6'),
          ],
        ),
      ],
    ),
  ),
);

leftColumn 变量包含评级和图标行,以及蛋糕介绍的标题和文本:

The leftColumn variable contains the ratings and icons rows, as well as the title and text that describes the Pavlova:

final leftColumn = Container(
  padding: EdgeInsets.fromLTRB(20, 30, 20, 20),
  child: Column(
    children: [
      titleText,
      subTitle,
      ratings,
      iconList,
    ],
  ),
);

左列放置在 Container 中以限制其宽度。最后,UI 由 Card 内的整行(包含左列和图像)构成。

The left column is placed in a Container to constrain its width. Finally, the UI is constructed with the entire row (containing the left column and the image) inside a Card.

蛋糕图片 来自 Pixabay。你可以使用 Image.network() 从网络上引用图像,但是在本例图像将保存到项目中的一个图像目录中,添加到 pubspec 文件,并使用 Images.asset() 访问。更多信息可以查看文档中关于 添加资源和图片 这一章。

The Pavlova image is from Pixabay. You can embed an image from the net using Image.network() but, for this example, the image is saved to an images directory in the project, added to the pubspec file, and accessed using Images.asset(). For more information, see Adding assets and images.

body: Center(
  child: Container(
    margin: EdgeInsets.fromLTRB(0, 40, 0, 30),
    height: 600,
    child: Card(
      child: Row(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Container(
            width: 440,
            child: leftColumn,
          ),
          mainImage,
        ],
      ),
    ),
  ),
),

App 源码: pavlova

App source: pavlova


通用布局 widgets

Common layout widgets

Flutter 有一个丰富的布局 widget 仓库,里面有很多经常会用到的布局 widget。目的是为了让你更快的上手,而不是被一个完整的列表吓跑。关于其他有用的 widget 的信息,可以参考 Widget 目录,或者使用 API 参考文档 中的搜索框。而且,API 文档中的 widget 页面中经常会给出一些关于相似的 widget 哪个会更适合你的建议。

Flutter has a rich library of layout widgets. Here are a few of those most commonly used. The intent is to get you up and running as quickly as possible, rather than overwhelm you with a complete list. For information on other available widgets, refer to the Widget catalog, or use the Search box in the API reference docs. Also, the widget pages in the API docs often make suggestions about similar widgets that might better suit your needs.

下面的 widget 会分为两类:widgets 库 中的标准 widgets 和 Material 库 中的 widgets。任何 app 都可以使用 widget 库,但是 Material 库中的组件只能在 Material app 中使用。

The following widgets fall into two categories: standard widgets from the widgets library, and specialized widgets from the Material library. Any app can use the widgets library but only Material apps can use the Material Components library.

标准 widgets

Standard widgets

  • Container:向 widget 增加 padding、margins、borders、background color 或者其他的装饰。

    Container: Adds padding, margins, borders, background color, or other decorations to a widget.

  • GridView:将 widget 展示为一个可滚动的网格。

    GridView: Lays widgets out as a scrollable grid.

  • ListView:将 widget 展示为一个可滚动的列表。

    ListView: Lays widgets out as a scrollable list.

  • Stack:将 widget 覆盖在另一个的上面。

    Stack: Overlaps a widget on top of another.

Material widgets

  • Card:将相关信息整理到一个有圆角和阴影的盒子中。

    Card: Organizes related info into a box with rounded corners and a drop shadow.

  • ListTile:将最多三行的文本、可选的导语以及后面的图标组织在一行中。

    ListTile: Organizes up to 3 lines of text, and optional leading and trailing icons, into a row.

Container

许多布局都可以随意的用 Container,它可以将使用了 padding 或者增加了 borders/margins 的 widget 分开。你可以通过将整个布局放到一个 Container 中,并且改变它的背景色或者图片,来改变设备的背景。

Many layouts make liberal use of Containers to separate widgets using padding, or to add borders or margins. You can change the device’s background by placing the entire layout into a Container and changing its background color or image.

摘要 (Container)

Summary (Container)

  • 增加 padding、margins、borders

    Add padding, margins, borders

  • 改变背景色或者图片

    Change background color or image

  • 只包含一个子 widget,但是这个子 widget 可以是行、列或者是 widget 树的根 widget

    Contains a single child widget, but that child can be a Row, Column, or even the root of a widget tree

Diagram showing: margin, border, padding, and content

示例 (Container)

Examples (Container)

这个布局包含一个有两行的列,每行有两张图片。Container 用来将列的背景色变为浅灰色。

This layout consists of a column with two rows, each containing 2 images. A Container is used to change the background color of the column to a lighter grey.

Widget _buildImageColumn() => Container(
      decoration: BoxDecoration(
        color: Colors.black26,
      ),
      child: Column(
        children: [
          _buildImageRow(1),
          _buildImageRow(3),
        ],
      ),
    );
Screenshot showing 2 rows, each containing 2 images

Container 还用来为每个图片添加圆角和外边距:

A Container is also used to add a rounded border and margins to each image:

Widget _buildDecoratedImage(int imageIndex) => Expanded(
      child: Container(
        decoration: BoxDecoration(
          border: Border.all(width: 10, color: Colors.black38),
          borderRadius: const BorderRadius.all(const Radius.circular(8)),
        ),
        margin: const EdgeInsets.all(4),
        child: Image.asset('images/pic$imageIndex.jpg'),
      ),
    );

Widget _buildImageRow(int imageIndex) => Row(
      children: [
        _buildDecoratedImage(imageIndex),
        _buildDecoratedImage(imageIndex + 1),
      ],
    );

你可以在 布局构建教程Flutter Gallery 中可以发现更多关于 Container 的例子。

You can find more Container examples in the tutorial and the Flutter Gallery.

App 源码: container

App source: container


GridView

使用 GridView 将 widget 作为二维列表展示。GridView 提供两个预制的列表,或者你可以自定义网格。当 GridView 检测到内容太长而无法适应渲染盒时,它就会自动支持滚动。

Use GridView to lay widgets out as a two-dimensional list. GridView provides two pre-fabricated lists, or you can build your own custom grid. When a GridView detects that its contents are too long to fit the render box, it automatically scrolls.

摘要 (GridView)

Summary (GridView)

  • 在网格中使用 widget

    Lays widgets out in a grid

  • 当列的内容超出渲染容器的时候,它会自动支持滚动。

    Detects when the column content exceeds the render box and automatically provides scrolling

  • 创建自定义的网格,或者使用下面提供的网格的其中一个:

    Build your own custom grid, or use one of the provided grids:

    • GridView.count 允许你制定列的数量

      GridView.count allows you to specify the number of columns

    • GridView.extent 允许你制定单元格的最大宽度

      GridView.extent allows you to specify the maximum pixel width of a tile

示例 (GridView)

Examples (GridView)

A 3-column grid of photos

使用 GridView.extent 创建一个最大宽度为 150 像素的网格。

Uses GridView.extent to create a grid with tiles a maximum 150 pixels wide.

App 源码: grid_and_list

App source: grid_and_list

A 2 column grid with footers

使用 GridView.count 创建一个网格,它在竖屏模式下有两行,在横屏模式下有三行。可以通过为每个 GridTile 设置 footer 属性来创建标题。

Uses GridView.count to create a grid that’s 2 tiles wide in portrait mode, and 3 tiles wide in landscape mode. The titles are created by setting the footer property for each GridTile.

Dart 代码: Flutter Gallery 中的 grid_list_demo.dart

Dart code: grid_list_demo.dart from the Flutter Gallery

Widget _buildGrid() => GridView.extent(
    maxCrossAxisExtent: 150,
    padding: const EdgeInsets.all(4),
    mainAxisSpacing: 4,
    crossAxisSpacing: 4,
    children: _buildGridTileList(30));

// The images are saved with names pic0.jpg, pic1.jpg...pic29.jpg.
// The List.generate() constructor allows an easy way to create
// a list when objects have a predictable naming pattern.
List<Container> _buildGridTileList(int count) => List.generate(
    count, (i) => Container(child: Image.asset('images/pic$i.jpg')));

ListView

ListView,一个和列很相似的 widget,当内容长于自己的渲染盒时,就会自动支持滚动。

ListView, a column-like widget, automatically provides scrolling when its content is too long for its render box.

摘要 (ListView)

Summary (ListView)

  • 一个用来组织盒子中列表的专用 Column

    A specialized Column for organizing a list of boxes

  • 可以水平或者垂直布局

    Can be laid out horizontally or vertically

  • 当监测到空间不足时,会提供滚动

    Detects when its content won’t fit and provides scrolling

  • Column 的配置少,使用更容易,并且支持滚动

    Less configurable than Column, but easier to use and supports scrolling

示例 (ListView)

Examples (ListView)

ListView containing movie theaters and restaurants

使用 ListView 的业务列表,它使用了多个 ListTileDivider 将餐厅从剧院中分隔开。

Uses ListView to display a list of businesses using ListTiles. A Divider separates the theaters from the restaurants.

App 源码: grid_and_list

App source: grid_and_list

ListView containing shades of blue

使用 ListView 展示特定颜色系列 Material Design 调色板 中的 颜色

Uses ListView to display the Colors from the Material Design palette for a particular color family.

Dart 代码: Flutter Gallery 中的 colors_demo.dart

Dart code: colors_demo.dart from the Flutter Gallery

Widget _buildList() => ListView(
      children: [
        _tile('CineArts at the Empire', '85 W Portal Ave', Icons.theaters),
        _tile('The Castro Theater', '429 Castro St', Icons.theaters),
        _tile('Alamo Drafthouse Cinema', '2550 Mission St', Icons.theaters),
        _tile('Roxie Theater', '3117 16th St', Icons.theaters),
        _tile('United Artists Stonestown Twin', '501 Buckingham Way',
            Icons.theaters),
        _tile('AMC Metreon 16', '135 4th St #3000', Icons.theaters),
        Divider(),
        _tile('Kescaped_code#39;s Kitchen', '757 Monterey Blvd', Icons.restaurant),
        _tile('Emmyescaped_code#39;s Restaurant', '1923 Ocean Ave', Icons.restaurant),
        _tile(
            'Chaiya Thai Restaurant', '272 Claremont Blvd', Icons.restaurant),
        _tile('La Ciccia', '291 30th St', Icons.restaurant),
      ],
    );

ListTile _tile(String title, String subtitle, IconData icon) => ListTile(
      title: Text(title,
          style: TextStyle(
            fontWeight: FontWeight.w500,
            fontSize: 20,
          )),
      subtitle: Text(subtitle),
      leading: Icon(
        icon,
        color: Colors.blue[500],
      ),
    );

Stack

可以使用 Stack 在基础 widget(通常是图片)上排列 widget。widget 可以完全或者部分覆盖基础 widget。

Use Stack to arrange widgets on top of a base widget—often an image. The widgets can completely or partially overlap the base widget.

摘要 (Stack)

Summary (Stack)

  • 用于覆盖另一个 widget

    Use for widgets that overlap another widget

  • 子列表中的第一个 widget 是基础 widget;后面的子项覆盖在基础 widget 的顶部

    The first widget in the list of children is the base widget; subsequent children are overlaid on top of that base widget

  • Stack 的内容是无法滚动的

    A Stack’s content can’t scroll

  • 你可以编辑超出渲染框的子项

    You can choose to clip children that exceed the render box

示例 (Stack)

Examples (Stack)

Circular avatar image with a label

CircleAvatar 的上面使用 Stack 覆盖 Container(在透明的黑色背景上展示它的 Text)。Stack 使用 alignment 属性和 Alignment 让文本偏移。

Uses Stack to overlay a Container (that displays its Text on a translucent black background) on top of a CircleAvatar. The Stack offsets the text using the alignment property and Alignments.

App 源码: card_and_stack

App source: card_and_stack

An image with a grey gradient across the top

使用 Stack 将渐变叠加到图片的顶部。渐变可以将工具栏的图标和图片区分开来。

Uses Stack to overlay a gradient to the top of the image. The gradient ensures that the toolbar’s icons are distinct against the image.

Dart 代码: Flutter Gallery 中的 contacts_demo.dart

Dart code: contacts_demo.dart from the Flutter Gallery

Widget _buildStack() => Stack(
    alignment: const Alignment(0.6, 0.6),
    children: [
      CircleAvatar(
        backgroundImage: AssetImage('images/pic.jpg'),
        radius: 100,
      ),
      Container(
        decoration: BoxDecoration(
          color: Colors.black45,
        ),
        child: Text(
          'Mia B',
          style: TextStyle(
            fontSize: 20,
            fontWeight: FontWeight.bold,
            color: Colors.white,
          ),
        ),
      ),
    ],
  );

Card

Material 库 中的 Card 包含相关有价值的信息,几乎可以由任何 widget 组成,但是通常和 ListTile 一起使用。 Card 只有一个子项,这个子项可以是列、行、列表、网格或者其他支持多个子项的 widget。默认情况下,Card 的大小是 0x0 像素。你可以使用 SizedBox 控制 card 的大小。

A Card, from the Material library, contains related nuggets of information and can be composed from almost any widget, but is often used with ListTile. Card has a single child, but its child can be a column, row, list, grid, or other widget that supports multiple children. By default, a Card shrinks its size to 0 by 0 pixels. You can use SizedBox to constrain the size of a card.

在 Flutter 中,Card 有轻微的圆角和阴影来使它具有 3D 效果。改变 Cardelevation 属性可以控制阴影效果。例如,把 elevation 设置为 24,可以从视觉上更多的把 Card 抬离表面,使阴影变得更加分散。关于支持的 elevation 的值的列表,可以查看 Material guidelines 中的 Elevation。使用不支持的值则会使阴影无效。

In Flutter, a Card features slightly rounded corners and a drop shadow, giving it a 3D effect. Changing a Card’s elevation property allows you to control the drop shadow effect. Setting the elevation to 24, for example, visually lifts the Card further from the surface and causes the shadow to become more dispersed. For a list of supported elevation values, see Elevation in the Material guidelines. Specifying an unsupported value disables the drop shadow entirely.

摘要 (Card)

Summary (Card)

  • 实现一个 Material card

    Implements a Material card

  • 用于呈现相关有价值的信息

    Used for presenting related nuggets of information

  • 接收单个子项,但是子项可以是 RowColumn 或者其他可以包含列表子项的 widget

    Accepts a single child, but that child can be a Row, Column, or other widget that holds a list of children

  • 显示圆角和阴影

    Displayed with rounded corners and a drop shadow

  • Card 的内容无法滚动

    A Card’s content can’t scroll

  • 来自 Material 库

    From the Material library

示例 (Card)

Examples (Card)

Card containing 3 ListTiles

包含 3 个 ListTile 的 Card,并且通过被 SizedBox 包住来调整大小。Divider 分隔了第一个和第二个 ListTiles

A Card containing 3 ListTiles and sized by wrapping it with a SizedBox. A Divider separates the first and second ListTiles.

App 源码: card_and_stack

App source: card_and_stack

Card containing an image, text and buttons

包含图片和文本的 Card

A Card containing an image and text.

Dart 代码: Flutter Gallery 中的 cards_demo.dart

Dart code: cards_demo.dart from the Flutter Gallery

Widget _buildCard() => SizedBox(
    height: 210,
    child: Card(
      child: Column(
        children: [
          ListTile(
            title: Text('1625 Main Street',
                style: TextStyle(fontWeight: FontWeight.w500)),
            subtitle: Text('My City, CA 99984'),
            leading: Icon(
              Icons.restaurant_menu,
              color: Colors.blue[500],
            ),
          ),
          Divider(),
          ListTile(
            title: Text('(408) 555-1212',
                style: TextStyle(fontWeight: FontWeight.w500)),
            leading: Icon(
              Icons.contact_phone,
              color: Colors.blue[500],
            ),
          ),
          ListTile(
            title: Text('costa@example.com'),
            leading: Icon(
              Icons.contact_mail,
              color: Colors.blue[500],
            ),
          ),
        ],
      ),
    ),
  );

ListTile

ListTileMaterial 库 中专用的行 widget,它可以很轻松的创建一个包含三行文本以及可选的行前和行尾图标的行。ListTileCard 或者 ListView 中最常用,但是也可以在别处使用。

Use ListTile, a specialized row widget from the Material library, for an easy way to create a row containing up to 3 lines of text and optional leading and trailing icons. ListTile is most commonly used in Card or ListView, but can be used elsewhere.

摘要 (ListTile)

Summary (ListTile)

  • 一个可以包含最多 3 行文本和可选的图标的专用的行

    A specialized row that contains up to 3 lines of text and optional icons

  • Row 更少的配置,更容易使用

    Less configurable than Row, but easier to use

  • 来自 Material 库

    From the Material library

示例 (ListTile)

Examples (ListTile)

Card containing 3 ListTiles

包含 3 个 ListTilesCard

A Card containing 3 ListTiles.

App 源码: card_and_stack

App source: card_and_stack

3 ListTiles, each containing a pull-down button

使用 ListTile 列出 3 个下拉按钮类型。

Uses ListTile to list 3 drop down button types.

Dart 代码: Flutter Gallery 中的 buttons_demo.dart

Dart code: buttons_demo.dart from the Flutter Gallery


视频

Videos

下面的视频是 Flutter in Focus 系列的一部分,解释了 Stateless 和 Stateful 的 widget。

The following videos, part of the Flutter in Focus series, explain Stateless and Stateful widgets.

Flutter in Focus playlist


每周 widget 系列 的每一集都会关注一个 widget。其中会包含布局 widget。

Each episode of the Widget of the Week series focuses on a widget. Several of them includes layout widgets.

Flutter Widget of the Week playlist

其他资源

Other resources

当写布局代码时,下面的资源可能会帮助到你。

The following resources might help when writing layout code.