[译] MDC-102 Flutter:Material 结构和布局(Flutter)

MDC-102 Flutter:Material 结构和布局(Flutter)

1. 介绍

Material Components(MDC)帮助开发者实现 Material Design。MDC 由谷歌团队的工程师和 UX 设计师创造,为 Android、iOS、Web 和 Flutter 提供不少美观实用的 UI 组件。

material.io/develophtml

在教程 MDC-101 中,你使用了两个 Material 组件:文本框和墨水波纹效果的按钮来构建一个登录页面。如今让咱们经过添加导航、结构和数据来拓展应用。前端

你将要构建

在本教程中,你将为 Shrine —— 一个销售服装和家居用品的电子商务应用程序构建一个主页面。它将含有:android

  • 一个位于顶部的应用栏
  • 一个由产品填充的网格列表

这是四篇教程里的第二篇,它将引导你为 Shrine 的产品构建应用程序。咱们建议你按照教程的顺序一步一步地编写你的代码。ios

相关的教程能够在如下位置找到:git

到 MDC-104 的最后,你将会构建一个像这样的应用:github

将要用到的 MDC 组件

  • 顶部应用栏(Top app bar)
  • 网格(Grid)
  • 卡片(Card)

本教程中,你将使用 MDC-Flutter 提供的默认组件。你将会在 MDC-103: Material Design Theming 的颜色、形状、高度和类型中学习如何定制它们。后端

你将须要

  • Flutter SDK
  • 安装好 Flutter 插件的 Android Studio,或者你喜欢的代码编辑器
  • 示例代码

要在 iOS 上构建和运行 Flutter 应用程序,你须要知足如下要求:

  • 运行 macOS 的计算机
  • Xcode 9 或更新版本
  • iOS 模拟器,或者 iOS 物理设备

要在 Android 上构建和运行 Flutter 应用程序,你须要知足如下要求:

  • 运行 macOS、Windows 或 Linux 的计算机
  • Android Studio
  • Android 模拟器(随 Android Studio 一块儿提供)或 Android 物理设备

2. 安装 Flutter 环境

前提条件

要开始使用 Flutter 开发移动应用程序,你须要:bash

  • Flutter SDK
  • 装有 Flutter 插件的 IntelliJ IDE,或者你喜欢的代码编辑器

Flutter 的 IDE 工具适用于 Android StudioIntelliJ IDEA Community(免费)和 IntelliJ IDEA Ultimateapp

要在 iOS 上构建和运行 Flutter 应用程序,你须要知足如下要求:

  • 运行 macOS 的计算机
  • Xcode 9 或更新版本
  • iOS 模拟器,或者 iOS 物理设备

要在 Android 上构建和运行 Flutter 应用程序,你须要知足如下要求:

  • 运行 macOS、Windows 或者 Linux 的计算机
  • Android Studio
  • Android 模拟器(随 Android Studio 一块儿提供)或 Android 物理设备

获取详细的 Flutter 安装信息编辑器

重要提示:若是链接到计算机的 Android 手机上出现“容许 USB 调试”对话框,请启用始终容许今后计算机选项,而后单击肯定

在继续本教程以前,请确保你的 SDK 处于正确的状态。若是以前安装过 Flutter,则使用 flutter upgrade 来确保 SDK 处于最新版本。

flutter upgrade
复制代码

运行 flutter upgrade 将自动运行 flutter doctor。若是这是首次安装 Flutter 且不需升级,那么请手动运行 flutter doctor。查看显示的全部检查标记;这将会下载你须要的任何缺乏的 SDK 文件,并确保你的计算机配置无误以进行 Flutter 的开发。

flutter doctor
复制代码

3. 下载教程初始应用程序

从 MDC-101 继续?

若是你完成了 MDC-101,那么本教程所需代码应该已经准备就绪,跳转到 添加应用栏 步骤。

从头开始?

下载初始应用程序

下载初始程序

此入门程序位于 material-components-flutter-codelabs-102-starter_and_101-complete/mdc_100_series 目录中。

...或者从 GitHub 克隆它

要从 GitHub 克隆此项目,请运行如下命令:

git clone https://github.com/material-components/material-components-flutter-codelabs.git
cd material-components-flutter-codelabs
git checkout 102-starter_and_101-complete
复制代码

更多帮助:从 GitHub 上克隆存储库

正确的分支

教程 MDC-101 到 104 连续构建。因此当你完成 102 的代码后,它将变成 103 教程的初始代码!代码被分红不一样的分支,你可使用如下命令将它们所有列出:

git branch --list

要查看完整代码,请切换到 103-starter_and_102-complete 分支。

创建你的项目

如下步骤默认你使用的是 Android Studio (IntelliJ)。

建立项目

  1. 在终端中,导航到 material-components-flutter-codelabs

  2. 运行 flutter create mdc_100_series

打开项目

  1. 打开 Android Studio。

  2. 若是你看到欢迎页面,单击 打开已有的 Android Studio 项目

  1. 导航到 material-components-flutter-codelabs/mdc_100_series 目录并单击打开,这将打开此项目。

在构建项目一次以前,你能够忽略在分析中见到的任何错误。

  1. 在左侧的项目面板中,删除测试文件 ../test/widget_test.dart

  1. 若是出现提示,安装全部平台和插件更新或 FlutterRunConfigurationType,而后从新启动 Android Studio。

提示:确保你已安装 Flutter 和 Dart 插件

运行初始程序

如下步骤默认你在 Android 模拟器或设备上进行测试。你也能够在 iOS 模拟器或设备上进行,只要你安装了 Xcode。

  1. 选择设备或模拟器

若是 Android 模拟器还没有运行,请选择 Tools -> Android -> AVD Manager建立您设备并启动模拟器。若是 AVD 已存在,你能够直接在 IntelliJ 的设备选择器中启动模拟器,以下一步所示。

(对于 iOS 模拟器,若是它还没有运行,经过选择 Flutter Device Selection -> Open iOS Simulator 来在你的开发设备上启动它。)

  1. 启动 Flutter 应用:
  • 在你的编辑器窗口顶部寻找 Flutter Device Selection 下拉菜单,而后选择设备(例如,iPhone SE / Android SDK built for )。
  • 点击运行图标(
    )。

若是你没法成功运行此应用程序,停下来解决你的开发环境问题。尝试导航到 material-components-flutter-codelabs;若是你在终端中下载 .zip 文件,导航到 material-components-flutter-codelabs-... 而后运行 flutter create mdc_100_series

成功!Shrine 的初始登录代码应该在你的模拟器中运行了。你能够看到 Shrine 的 logo 和它下面的名称 "Shrine"。

如今登陆页面看起来不错,让咱们用一些产品来填充应用。

4. 添加顶部应用栏

当登录页面消失时主页面将出现并显示“你作到了!”。这很棒!可是咱们的用户不知道能作什么操做,也不知道如今位于应用何处,为了解决这个问题,是时候添加导航了。

导航 是指容许用户在应用中移动的组件、交互、视觉提示和信息结构。它使得内容和功能更加注目,任务也所以易于完成。

在 Material 指南中了解更多有关导航的信息。

Material Design 提供确保高度可用性的导航模式,其中最注目的组件就是顶部应用栏。

你能够将顶部应用栏看成 iOS 中的“导航栏”,或者简单当作一个 “App Bar” 或 “Header”。

要提供导航并让用户快速访问其余操做,让咱们添加一个顶部应用栏。

添加应用栏部件

home.dart 中,将应用栏添加到 Scaffold 中:

return Scaffold(
  // TODO: 添加应用栏(102)
  appBar: AppBar(
    // TODO: 添加按钮和标题(102)
  ),
复制代码

AppBar 添加到 Scaffold 的 appBar: 字段位置,为了咱们完美的布局,让应用栏保持在页面的顶部或底部。

Scaffold 在中是一个重要的部件。它为像抽屉、snack bar 和 bottom sheet 等各类常见 Material 组件提供方便的 API。它甚至能够帮助布置一个 Floating Action Button。

Flutter 文档中了解更多有关 Scaffold 的信息。

保存项目,当 Shrine 应用更新后,单击 Next 来查看主屏幕。

应用栏看起来不错,但它还须要一个标题。

若是应用没有更新,再次单击 “Play” 按钮,或者点击 “Play” 后的 “Stop”。

添加文本部件

home.dart 中,给应用栏添加一个标题:

// TODO: 添加应用栏(102)  
  appBar: AppBar(
    // TODO: 添加按钮和标题(102)

    title: Text('SHRINE'),
        // TODO:添加后续按钮(102)
复制代码

保存项目。

到目前为止,你应该已经注意到咱们所说的“平台差别”了。Material 明白 Android、iOS、Web 各平台都有差别。用户对他们有不一样的指望。举例来讲,在 iOS 里标题几乎老是居中的,这是 UIKit 提供的默认配置。在 Android 上标题是左对齐的。因此若是你使用的是 Android 模拟器或设备,那么标题应该位于左侧,对于 iOS 模拟器和设备而言,它应该是居中的。

了解更多信息,请查参阅有关跨平台适配的 Material 文章

许多应用栏在标题旁边都设有按钮,让咱们在应用中添加一个菜单图标。

添加位于首部的图标按钮

仍是在 home.dart 中,在 AppBar 的 leading 字段设置一个图标按钮:(放在 title: 字段前,按照部件从首到尾的顺序):

return Scaffold(
  appBar: AppBar(
    // TODO: 添加按钮和标题(102)
    leading: IconButton(
      icon: Icon(
        Icons.menu,
        semanticLabel: 'menu',
      ),
      onPressed: () {
        print('Menu button');
      },
    ),
复制代码

保存项目。

菜单图标(也被称做“汉堡包”)会在你指望的位置显示出来。

IconButton 类是在你的应用里引入 Material 图标的快捷方式。它有一个 Icon 部件。 Flutter 在 Icons 类里有整套的图标。它会根据字符串常量的映射自动导入图标。

Flutter 文档中了解更多有关 Icons 类的信息。有关 Icon 部件的信息请阅读这个 Flutter 文档

你也能够在标题尾部添加按钮。在 Flutter 中,它们被称为 "action"。

Leading(首部)trailing(尾部) 是表达方向的术语,指的是与语言无关的文本行的开头和结尾。当使用一个像英语这样的 LTR(左到右)语言时, leading 意味着 左侧trailing 表明着 右侧。在像阿拉伯语这样的 RTL(右到左)语言时, leading 意味着 右侧trailing 表明着 左侧

了解 UI 镜像的更多信息,请参阅 双向性 Material Design 准则。

添加 action

还有两个 IconButton 的空间。

在 AppBar 实例中的标题后面添加它们:

// TODO: 添加尾部按钮(102)
actions: <Widget>[
  IconButton(
    icon: Icon(
      Icons.search,
      semanticLabel: 'search',
    ),
    onPressed: () {
      print('Search button');
    },
  ),
  IconButton(
    icon: Icon(
      Icons.tune,
      semanticLabel: 'filter',
    ),
    onPressed: () {
      print('Filter button');
    },
  ),
],
复制代码

保存你的项目。你的主屏幕看起来应该像这样:

如今这个应用在左侧有一个按钮、一个标题,右侧还有两个 action。应用栏还利用阴影显示高度,表示它与内容处于不一样的层级。

在 Icon 类中,SemanticLabel 字段是在 Flutter 中添加辅助功能信息的经常使用方法。这很像 Android 的 Content Label 或 iOS 的 UIAccessibility accessibilityLabel。你会在不少类中见到它。

这个字段的信息很好地向使用屏幕阅读器的人说明了该按钮的做用。

对于没有 semanticLabel: 字段的部件,你能够将其包装在 Semantics 部件中,在其 Flutter 文档中了解更多有关的信息。

5. 在网格中添加卡片

如今咱们的应用像点样子了,让咱们接着放置一些卡片来组织内容。

卡片 是显示单体内容和动做的独立的元素。它们是一种能够灵活地呈现近似内容集合的方式。

在 Material 指南有关卡片的文章中了解更多信息。

要了解卡片部件,请参阅在 Flutter 中构建布局

添加网格视图

让咱们从应用栏底部添加一个卡片开始。单一的 卡片 部件不足以让咱们将它放到咱们想要的位置,因此咱们须要将它封装在一个 网格视图 中。

用 GridView 替换 Scaffold 中 body 字段的 Center:

// TODO: 添加网格视图(102)
body: GridView.count(
  crossAxisCount: 2,
  padding: EdgeInsets.all(16.0),
  childAspectRatio: 8.0 / 9.0,
  // TODO: 构建一组卡片(102)
  children: <Widget>[Card()],
),
复制代码

让咱们分析这段代码。网格视图调用 count() 构造函数,因要添加的项目数是可数的而不是无限的。但它须要更多信息来定义其布局。

crossAxisCount: 指定横向显示数目,咱们设置成 2 列。

Flutter 中的 Cross axis(横轴) 表示非滚动轴。可滚动的方向称为 主轴。因此若是你的应用像网格视图默认的那样垂直滚动,那么横轴就是水平方向。

详情请参阅构建布局

padding: 字段为网格视图的 4 条边设置填充。固然你如今看不到首尾的填充,由于网格视图内尚未其余子项。

childAspectRatio: 字段依据宽高比肯定其大小。

默认地,网格视图中的项目尺寸相同。

将这些加在一块儿,网格视图按照以下方式计算每一个子项的宽度:([整个网格宽度] - [左填充] - [右填充]) / 列数。在这里就是:([整个网格宽度] - 16 - 16) / 2

高度是根据宽度计算得来的,经过应用宽高比:([整个网格宽度] - 16 - 16) / 2 * 9 / 8。咱们翻转了 8 和 9,由于咱们是用宽度来计算高度。

咱们已经有了一个空的卡片了,让咱们添加一些子部件到卡片中。

布局内容

卡片内应该包含一张图片、一个标题和一个次级文本。

更新网格视图的子项:

// TODO: 构建一组卡片(102)
children: <Widget>[
  Card(
    child: Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: <Widget>[
        AspectRatio(
          aspectRatio: 18.0 / 11.0,
          child: Image.asset('assets/diamond.png'),
        ),
        Padding(
          padding: EdgeInsets.fromLTRB(16.0, 12.0, 16.0, 8.0),
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: <Widget>[
              Text('Title'),
              SizedBox(height: 8.0),
              Text('Secondary Text'),
            ],
          ),
        ),
      ],
    ),
  )
],
复制代码

这段代码添加了一个列部件,用来垂直地布局子部件。

crossAxisAlignment: 字段指定 CrossAxisAlignment.start 属性,这意味着“文本与前沿对齐”。

AspectRatio 部件决定图像的形状,不管提供的是何种图像。

Padding 使得文本与边框保持必定距离。

两个 Text 部件垂直堆叠,在其间保持 8 个单位的间隔(SizedBox)。咱们使用另外一个 Column 来把它们放到 Padding 中。

保存你的项目:

在这个预览里,你能够看到卡片从边缘置入,并带有圆角和阴影(这表明着卡片的高度)。整个形状在 Material 中被称为 “container(容器)”。(不要与名为 Container 的实际部件类混淆。)

除了容器之外,在 Material 中卡片内全部的元素实际上都是可选的。你能够添加标题文本、缩略图、头像或者小标题文本、分隔符甚至是按钮和图标。

了解更多消息,请参阅 Material 指南上有关卡片的文章。

卡片常常以集合的形式和其余卡片一块儿出现,让咱们在网格视图中给它们布局。

6. 生成卡片集合

每当屏幕上出现多张卡片时,它们就会组成一个或多个集合。集合中的卡片是共面的,这意味着卡片共享相同的静止高度。(除了卡片被拾起或拖动,但在这里咱们不会这么作。)

将卡片添加到集合

如今咱们的卡片是网格视图内的 children: 字段子项。这有一大段难以阅读的嵌套代码。让咱们将它提取到一个函数中来生成任意数量的空卡片,而后返回给咱们。

// TODO: 生成卡片集合(102)
List<Card> _buildGridCards(int count) {
  List<Card> cards = List.generate(
    count,
    (int index) => Card(
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: <Widget>[
          AspectRatio(
            aspectRatio: 18.0 / 11.0,
            child: Image.asset('assets/diamond.png'),
          ),
          Padding(
            padding: EdgeInsets.fromLTRB(16.0, 12.0, 16.0, 8.0),
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: <Widget>[
                Text('Title'),
                SizedBox(height: 8.0),
                Text('Secondary Text'),
              ],
            ),
          ),
        ],
      ),
    ),
  );

  return cards;
}
复制代码

将生成的卡片分配给网格视图的 children 字段。记得用新代码替换网格视图中的全部内容。

// TODO: 添加网格视图(102)
body: GridView.count(
  crossAxisCount: 2,
  padding: EdgeInsets.all(16.0),
  childAspectRatio: 8.0 / 9.0,
  children: _buildGridCards(10) // 替换全部内容
),
复制代码

保存你的项目:

卡片已经在这了,但它们什么都没有显示。如今是时候添加一些产品数据了。

###添加产品数据

这个应用中的产品有着图像、名称和价格。让咱们把这些添加到已有的卡片部件中。

而后,在 home.dart 中,导入数据模型须要的新包和文件:

import 'package:flutter/material.dart';
import 'package:intl/intl.dart';

import 'model/products_repository.dart';
import 'model/product.dart';
复制代码

最后,更改 _buildGridCards() 来获取产品信息,并将数据应用到卡片中:

// TODO: 生成卡片集合(102)

// 替换整个方法
List<Card> _buildGridCards(BuildContext context) {
  List<Product> products = ProductsRepository.loadProducts(Category.all);

  if (products == null || products.isEmpty) {
    return const <Card>[];
  }

  final ThemeData theme = Theme.of(context);
  final NumberFormat formatter = NumberFormat.simpleCurrency(
      locale: Localizations.localeOf(context).toString());

  return products.map((product) {
    return Card(
      // TODO: 调整卡片高度(103)
      child: Column(
        // TODO: 卡片的内容设置居中(103)
        crossAxisAlignment: CrossAxisAlignment.start,
        children: <Widget>[
          AspectRatio(
            aspectRatio: 18 / 11,
            child: Image.asset(
              product.assetName,
              package: product.assetPackage,
             // TODO: 调整盒子尺寸(102)
            ),
          ),
          Expanded(
            child: Padding(
              padding: EdgeInsets.fromLTRB(16.0, 12.0, 16.0, 8.0),
              child: Column(
               // TODO: 标签底部对齐并居中(103)
               crossAxisAlignment: CrossAxisAlignment.start,
                // TODO: 更改最内部的列(103)
                children: <Widget>[
                 // TODO: 处理溢出的标签(103)
                 Text(
                    product.name,
                    style: theme.textTheme.title,
                    maxLines: 1,
                  ),
                  SizedBox(height: 8.0),
                  Text(
                    formatter.format(product.price),
                    style: theme.textTheme.body2,
                  ),
                ],
              ),
            ),
          ),
        ],
      ),
    );
  }).toList();
}
复制代码

注意:应用如今没法编译和运行,咱们还须要进行修改。

要设置文本的样式,咱们使用当前 BuildContext 中的 ThemeData

了解有关文本样式的更多信息,请参阅 Material 指南中的排版一文。了解有关主题的更多信息,请参考教程下一章 MDC-103: Material Design Theming 的颜色、形状、高度和类型

在尝试编译以前,将 BuildContext 传入 build() 方法中的 _buildGridCards()

// TODO: Add a grid view (102)
body: GridView.count(
  crossAxisCount: 2,
  padding: EdgeInsets.all(16.0),
  childAspectRatio: 8.0 / 9.0,
  children: _buildGridCards(context) // Changed code
),
复制代码

你可能注意到了咱们没有在卡片间添加任何垂直的间隔,这是由于在其顶部与底部默认有 4 个单位的填充。

保存你的项目:

产品的数据显示出来了,可是图像四周有额外的空间。图像默认依据 .scaleDownBoxFit 绘制(在这个状况下)。让咱们将其更改成 .fitWidth 来让它们放大一点,删除多余的空间。

修改图像的 fit: 字段:

// TODO: 调整盒子尺寸(102)
  fit: BoxFit.fitWidth,
复制代码

如今咱们的产品完美的展示在应用中了!

7. 总结

咱们的应用已经有了基本的流程,将用户从登录屏幕带到能够查看产品的主屏幕。经过几行代码,咱们添加了一个顶部应用栏(带有标题和三个按钮)以及卡片(用于显示咱们应用的内容)。咱们的主屏幕简洁实用,具备基本的结构和可操做的内容。

完成的 MDC-102 应用能够在 103-starter_and_102-complete 分支中找到。

你能够用此分支下的应用来对照验证你的版本。

下一步

经过顶部应用栏、卡片、文本框和按钮,咱们已经使用了 MDC-Flutter 库中的四个核心组件!你能够访问 Flutter 部件目录来探索更多组件。

虽然它彻底正常运行,咱们的应用还没有表达任何特殊的品牌特色。在 MDC-103: Material Design Theming 的颜色、形状、高度和类型中,咱们将定制这些组件的样式,来诠释一个充满活力的、现代的品牌。

若是发现译文存在错误或其余须要改进的地方,欢迎到 掘金翻译计划 对译文进行修改并 PR,也可得到相应奖励积分。文章开头的 本文永久连接 即为本文在 GitHub 上的 MarkDown 连接。


掘金翻译计划 是一个翻译优质互联网技术文章的社区,文章来源为 掘金 上的英文分享文章。内容覆盖 AndroidiOS前端后端区块链产品设计人工智能等领域,想要查看更多优质译文请持续关注 掘金翻译计划官方微博知乎专栏

相关文章
相关标签/搜索