- 原文地址:MDC-102 Flutter: Material Structure and Layout (Flutter)
- 原文做者:codelabs.developers.google.com
- 译文出自:掘金翻译计划
- 本文永久连接:github.com/xitu/gold-m…
- 译者:DevMcryYu
- 校对者:Rickon
material.io/develophtml
在教程 MDC-101 中,你使用了两个 Material 组件:文本框和墨水波纹效果的按钮来构建一个登录页面。如今让咱们经过添加导航、结构和数据来拓展应用。前端
在本教程中,你将为 Shrine —— 一个销售服装和家居用品的电子商务应用程序构建一个主页面。它将含有:android
这是四篇教程里的第二篇,它将引导你为 Shrine 的产品构建应用程序。咱们建议你按照教程的顺序一步一步地编写你的代码。ios
相关的教程能够在如下位置找到:git
- MDC-101: Material Components(MDC)基础
- MDC-103: Material Design Theming 的颜色、形状、高度和类型
- MDC-104: Material Design 高级组件。
到 MDC-104 的最后,你将会构建一个像这样的应用:github
![]()
本教程中,你将使用 MDC-Flutter 提供的默认组件。你将会在 MDC-103: Material Design Theming 的颜色、形状、高度和类型中学习如何定制它们。后端
要开始使用 Flutter 开发移动应用程序,你须要:bash
Flutter 的 IDE 工具适用于 Android Studio、IntelliJ IDEA Community(免费)和 IntelliJ IDEA Ultimate。app
重要提示:若是链接到计算机的 Android 手机上出现“容许 USB 调试”对话框,请启用始终容许今后计算机选项,而后单击肯定。
在继续本教程以前,请确保你的 SDK 处于正确的状态。若是以前安装过 Flutter,则使用 flutter upgrade
来确保 SDK 处于最新版本。
flutter upgrade
复制代码
运行 flutter upgrade
将自动运行 flutter doctor
。若是这是首次安装 Flutter 且不需升级,那么请手动运行 flutter doctor
。查看显示的全部检查标记;这将会下载你须要的任何缺乏的 SDK 文件,并确保你的计算机配置无误以进行 Flutter 的开发。
flutter doctor
复制代码
若是你完成了 MDC-101,那么本教程所需代码应该已经准备就绪,跳转到 添加应用栏 步骤。
此入门程序位于 material-components-flutter-codelabs-102-starter_and_101-complete/mdc_100_series
目录中。
要从 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)。
在终端中,导航到 material-components-flutter-codelabs
运行 flutter create mdc_100_series
打开 Android Studio。
若是你看到欢迎页面,单击 打开已有的 Android Studio 项目。
material-components-flutter-codelabs/mdc_100_series
目录并单击打开,这将打开此项目。在构建项目一次以前,你能够忽略在分析中见到的任何错误。
../test/widget_test.dart
提示:确保你已安装 Flutter 和 Dart 插件。
如下步骤默认你在 Android 模拟器或设备上进行测试。你也能够在 iOS 模拟器或设备上进行,只要你安装了 Xcode。
若是 Android 模拟器还没有运行,请选择 Tools -> Android -> AVD Manager 来建立您设备并启动模拟器。若是 AVD 已存在,你能够直接在 IntelliJ 的设备选择器中启动模拟器,以下一步所示。
(对于 iOS 模拟器,若是它还没有运行,经过选择 Flutter Device Selection -> Open iOS Simulator 来在你的开发设备上启动它。)
若是你没法成功运行此应用程序,停下来解决你的开发环境问题。尝试导航到
material-components-flutter-codelabs
;若是你在终端中下载 .zip 文件,导航到material-components-flutter-codelabs-...
而后运行flutter create mdc_100_series
。
成功!Shrine 的初始登录代码应该在你的模拟器中运行了。你能够看到 Shrine 的 logo 和它下面的名称 "Shrine"。
如今登陆页面看起来不错,让咱们用一些产品来填充应用。
当登录页面消失时主页面将出现并显示“你作到了!”。这很棒!可是咱们的用户不知道能作什么操做,也不知道如今位于应用何处,为了解决这个问题,是时候添加导航了。
导航 是指容许用户在应用中移动的组件、交互、视觉提示和信息结构。它使得内容和功能更加注目,任务也所以易于完成。
在 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 准则。
还有两个 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 文档中了解更多有关的信息。
如今咱们的应用像点样子了,让咱们接着放置一些卡片来组织内容。
卡片 是显示单体内容和动做的独立的元素。它们是一种能够灵活地呈现近似内容集合的方式。
在 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 指南上有关卡片的文章。
卡片常常以集合的形式和其余卡片一块儿出现,让咱们在网格视图中给它们布局。
每当屏幕上出现多张卡片时,它们就会组成一个或多个集合。集合中的卡片是共面的,这意味着卡片共享相同的静止高度。(除了卡片被拾起或拖动,但在这里咱们不会这么作。)
如今咱们的卡片是网格视图内的 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 个单位的填充。
保存你的项目:
产品的数据显示出来了,可是图像四周有额外的空间。图像默认依据 .scaleDown
的 BoxFit 绘制(在这个状况下)。让咱们将其更改成 .fitWidth
来让它们放大一点,删除多余的空间。
修改图像的 fit:
字段:
// TODO: 调整盒子尺寸(102)
fit: BoxFit.fitWidth,
复制代码
如今咱们的产品完美的展示在应用中了!
咱们的应用已经有了基本的流程,将用户从登录屏幕带到能够查看产品的主屏幕。经过几行代码,咱们添加了一个顶部应用栏(带有标题和三个按钮)以及卡片(用于显示咱们应用的内容)。咱们的主屏幕简洁实用,具备基本的结构和可操做的内容。
完成的 MDC-102 应用能够在
103-starter_and_102-complete
分支中找到。你能够用此分支下的应用来对照验证你的版本。
经过顶部应用栏、卡片、文本框和按钮,咱们已经使用了 MDC-Flutter 库中的四个核心组件!你能够访问 Flutter 部件目录来探索更多组件。
虽然它彻底正常运行,咱们的应用还没有表达任何特殊的品牌特色。在 MDC-103: Material Design Theming 的颜色、形状、高度和类型中,咱们将定制这些组件的样式,来诠释一个充满活力的、现代的品牌。
若是发现译文存在错误或其余须要改进的地方,欢迎到 掘金翻译计划 对译文进行修改并 PR,也可得到相应奖励积分。文章开头的 本文永久连接 即为本文在 GitHub 上的 MarkDown 连接。
掘金翻译计划 是一个翻译优质互联网技术文章的社区,文章来源为 掘金 上的英文分享文章。内容覆盖 Android、iOS、前端、后端、区块链、产品、设计、人工智能等领域,想要查看更多优质译文请持续关注 掘金翻译计划、官方微博、知乎专栏。