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

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: [
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: [Card()],
),

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

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

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

详情请参阅构建布局

padding: 字段为网格视图的 4 条边设置填充。当然你现在看不到首尾的填充,因为网格视图内还没有其他子项。

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

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

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

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

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

布局内容

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

更新网格视图的子项:

// TODO: 构建一组卡片(102)
children: [
Card(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
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: [
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 _buildGridCards(int count) {
List cards = List.generate(
count,
(int index) => Card(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
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: [
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 _buildGridCards(BuildContext context) {
List products = ProductsRepository.loadProducts(Category.all);

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

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

return products.map((product) {
return Card(
// TODO: 调整卡片高度(103)

最后

总而言之,Android开发行业变化太快,作为技术人员就要保持终生学习的态度,让学习力成为核心竞争力,所谓“活到老学到老”只有不断的学习,不断的提升自己,才能跟紧行业的步伐,才能不被时代所淘汰。

在这里我分享一份自己收录整理上述技术体系图相关的几十套腾讯、头条、阿里、美团等公司20年的面试题,把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节,由于篇幅有限,这里以图片的形式给大家展示一部分。

还有高级架构技术进阶脑图、Android开发面试专题资料,高级进阶架构资料 帮助大家学习提升进阶,也节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习。
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》点击传送门,即可获取!
能跟紧行业的步伐,才能不被时代所淘汰。

在这里我分享一份自己收录整理上述技术体系图相关的几十套腾讯、头条、阿里、美团等公司20年的面试题,把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节,由于篇幅有限,这里以图片的形式给大家展示一部分。

[外链图片转存中…(img-J6n2PUz6-1715543798314)]

[外链图片转存中…(img-ZPHHRVsg-1715543798315)]

[外链图片转存中…(img-B8mKUiZ7-1715543798315)]

还有高级架构技术进阶脑图、Android开发面试专题资料,高级进阶架构资料 帮助大家学习提升进阶,也节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习。
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》点击传送门,即可获取!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值