material.io/develophtml
如今可使用 MDC 来为你的应用程序定制远比之前独特的样式。Material Design 近期的更新使得设计师和开发者能够更灵活地表达他们的产品理念。前端
在教程 MDC-101 和 MDC-102 中,你使用 Material 组件(MDC)为一个名为 Shrine 的销售服装和家居用品的电子商务应用程序构建基础。这个应用的用户使用流程包括一个开始的登录页面,而后导航用户前往展现商品的主屏幕。android
在本教程中,你将会使用如下属性来定制 Shrine 应用:ios
这是四篇教程中的第三篇,来引导你构建 Shrine 应用。git
其他教程可在这里找到:github
到 MDC-104 的最后,你将会构建一个像这样的应用:后端
要开始使用 Flutter 开发移动应用程序,你须要:bash
Flutter 的 IDE 工具适用于 Android Studio、IntelliJ IDEA Community(免费)和 IntelliJ IDEA Ultimate。网络
重要提示: 若是链接到计算机的 Android 手机上出现“容许 USB 调试”对话框,请启用始终容许今后计算机选项,而后单击肯定。
在继续本教程以前,请确保你的 SDK 处于正确的状态。若是以前安装过 Flutter SDK,则使用 flutter upgrade
来确保 SDK 处于最新版本。
flutter upgrade
复制代码
运行 flutter upgrade
将自动运行 flutter doctor
。若是这是首次安装 Flutter 且不需升级,那么请手动运行 flutter doctor
。查看显示的全部 ✓ 标记;这将会下载你须要的任何缺乏的 SDK 文件,并确保你的计算机配置无误以进行 Flutter 的开发。
flutter doctor
复制代码
若是你完成了 MDC-102,那么本教程所需代码应该已经准备就绪,跳转到调整颜色一步。
此入门程序位于 material-components-flutter-codelabs-103-starter_and_102-complete/mdc_100_series
目录中。
要从 GitHub 克隆此项目,请运行如下命令:
git clone https://github.com/material-components/material-components-flutter-codelabs.git
cd material-components-flutter-codelabs
git checkout 103-starter_and_102-complete
复制代码
更多帮助:从 GitHub 上克隆存储库
正确的分支
教程 MDC-101 到 104 连续构建。因此当你完成 103 的代码后,它将变成 104 教程的初始代码!代码被分红不一样的分支,你可使用如下命令将它们所有列出:
git branch --list
要查看完整代码,请切换到
104-starter_and_103-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"。
若是应用没有更新,再次单击 “Play” 按钮,或者点击 “Play” 后的 “Stop”。
点击“Next”来查看上一教程中的主屏幕。
一个表明着 Shrine 品牌的配色方案已经建立好了。设计师但愿你在 Shrine 应用中实现这个配色方案。
首先,让咱们在项目里导入这些颜色。
colors.dart
在 lib
目录下新建一个名为 colors.dart
的 dart 文件。导入 Material 组件并添加 Color 常量:
import 'package:flutter/material.dart';
const kShrinePink50 = const Color(0xFFFEEAE6);
const kShrinePink100 = const Color(0xFFFEDBD0);
const kShrinePink300 = const Color(0xFFFBB8AC);
const kShrinePink400 = const Color(0xFFEAA4A4);
const kShrineBrown900 = const Color(0xFF442B2D);
const kShrineErrorRed = const Color(0xFFC5032B);
const kShrineSurfaceWhite = const Color(0xFFFFFBFA);
const kShrineBackgroundWhite = Colors.white;
复制代码
此颜色主题由设计师自选颜色进行建立(以下图所示)。它包含 Shrine 的品牌色并应用于 Material 主题编辑器,由此衍生出的完整的调色板。(这些颜色并不是来自 2014 Material color palette。)
Material 主题编辑器使用以数字表示的色度(shade)对颜色进行分类,每种颜色都有 50、100、200、... 一直到 900 等几个色度。Shrine 仅仅使用 50、100 和 300 色度的粉色调以及 900 色度的棕色调。
译者注:色度:色彩深浅、明暗的程度。
每一个部件的颜色参数都对应此模板内的颜色。例如,文本框在接收输入时的修饰颜色应该是主题的 Primary color。若是该颜色不合适(易于与背景区分),请改用 PrimaryVariant。
Colors 类
kShrineBackgroundWhite
的值来自于 Colors 类。这个类包含常见的颜色值,例如白色。它还包含 2014 color palette 做为 MaterialColor 类。MaterialColor 类
在 'material/colors.dart' 中找到的 MaterialColor 类(子类是 ColorSwatch)是一组包含 14 或更少种由原色变换成的颜色,好比 14 种不一样色度的红色、绿色、浅绿或石灰色。这就相似于你在油漆店里见到的渐变色色卡。
这些颜色是在 2014 Material 指南中提出的,而且在当前指南(颜色系统(Color System))以及 MDC-Flutter 中仍然可用。要在代码里访问它们,只需调用基础色后接色度(一般为 100 的倍数)便可。例如,Pink 400 可经过
Colors.pink[400]
检索到。你彻底能够将这些调色盘运用到你的设计和代码上。若是已经有属于品牌本身的配色,也可使用调色板生成工具或者Material 主题编辑器来生成本身的配色。
如今咱们有想用的颜色了。咱们能够将它应用到 UI 上。咱们将经过设置应用于 MaterialApp 实例顶部层次结构的 ThemeData 部件来实现。
Flutter 包含一些内置主题。light 主题就是其中之一。与其从零开始制做一个 ThemeData 部件,咱们不如拷贝 light 主题而后修改其中的一部分属性来为咱们的应用进行定制。
咱们在默认的 light ThemeData 中调用 copyWith()
,而后传入一些自定义属性值(copyWith()
在 Flutter 中是一个经常使用方法,你会在不少类和部件中看到它)。这个命令返回与调用它的实例匹配的部件实例,可是替换了一些指定的值。
为何不实例化一个 ThemeData 而后设它的属性呢?固然能够!若是咱们继续构建咱们的程序,这将颇有意义。因为 ThemeData 拥有大量的属性,为了节省时间,咱们的教程将从修改一个有吸引力的主题的可见值入手。当咱们稍后尝试使用替代主题时,咱们将从 MDC-Flutter 附带的 ThemeData 开始。
在 Flutter 文档中了解更多有关 ThemeData 的信息。
让咱们在 app.dart
中导入 colors.dart
。
import 'colors.dart';
复制代码
而后将如下内容添加到 app.dart 的 ShrineApp 类以外的地方:
// TODO:构建 Shrine 主题(103)
final ThemeData _kShrineTheme = _buildShrineTheme();
ThemeData _buildShrineTheme() {
final ThemeData base = ThemeData.light();
return base.copyWith(
accentColor: kShrineBrown900,
primaryColor: kShrinePink100,
buttonColor: kShrinePink100,
scaffoldBackgroundColor: kShrineBackgroundWhite,
cardColor: kShrineBackgroundWhite,
textSelectionColor: kShrinePink100,
errorColor: kShrineErrorRed,
// TODO:添加文本主题(103)
// TODO:添加图标主题(103)
// TODO:修饰输入内容(103)
);
}
复制代码
如今在应用的 build()
函数最后(在 MaterialApp 部件中)将 theme:
设成咱们的新主题:
// TODO:添加主题(103)
return MaterialApp(
title: 'Shrine',
// TODO:将 home: 改成 HomePage frontLayer(104)
home: HomePage(),
// TODO:让 currentCategory 字段持有 _currentCategory(104)
// TODO:向 frontLayer 传递 _currentCategory(104)
// TODO:将 backLayer 字段值改成 CategoryMenuPage(104)
initialRoute: '/login',
onGenerateRoute: _getRoute,
theme: _kShrineTheme, // 新加代码
);
复制代码
点击运行按钮,你的登录页面看起来应该是这个样子的:
你的主屏幕看起来应该像这样:
有关颜色(Color)和主题(Theme)的注意事项:
- 你能够自定义 UI 中的颜色以便诠释你的品牌特点。
- 从两种颜色(主要和次要颜色)开始制做调色板,使用不一样色度的颜色。或者使用 Material Design 调色盘工具生成。
- 不要忘记排版的颜色!
- 确保文本与背景的颜色对比度适中(主文本为 3:1,副文本为 4:1)
除了更改颜色,设计师还为咱们提供了特定的排版。Flutter 的 ThemeData 包含 3 种文本主题。每一个文本主题都是一个文本样式的集合,如 “headline” 和 “title”。咱们将为咱们的应用使用几种样式并更改一些值。
为了将字体导入项目,咱们必须将它们添加到 pubspec.yaml 文件中。
在 pubspec.yaml 中,在 flutter:
标签下添加如下内容:
# TODO:引入字体(103)
fonts:
- family: Rubik
fonts:
- asset: fonts/Rubik-Regular.ttf
- asset: fonts/Rubik-Medium.ttf
weight: 500
复制代码
如今你能够访问并使用 Rubik 字体了。
若是你剪切并粘贴上面的声明代码,你可能会在运行 pub get 时遇到错误。若是出现错误,请先删除前导空格,而后使用空格缩进替换空格。
fonts:
复制代码
以前有两个空格,
family: Rubik
复制代码
以前有四个空格,以此类推。
若是你看到 Mapping values are not allowed here(此处不容许存在映射值),检查问题所在行以及上方的其余行的缩进。
app.dart
中,在 _buildShrineTheme()
以后添加以下内容:
// TODO:构建 Shrine 文本主题(103)
TextTheme _buildShrineTextTheme(TextTheme base) {
return base.copyWith(
headline: base.headline.copyWith(
fontWeight: FontWeight.w500,
),
title: base.title.copyWith(
fontSize: 18.0
),
caption: base.caption.copyWith(
fontWeight: FontWeight.w400,
fontSize: 14.0,
),
).apply(
fontFamily: 'Rubik',
displayColor: kShrineBrown900,
bodyColor: kShrineBrown900,
);
}
复制代码
这须要一个文本主题而且更改 headline、titles 和 captions 的样式。
用这种方式应用 fontFamily
仅将更改应用于 copyWith()
字段中指定的(headline, title, caption)排版比例。
对于某些字体,咱们正在为其设置自定义 FontWeight。FontWeight 部件在 100s 上具备方便的值。在字体中,w500(权值(weight)500)是中等大小,w400 是常规大小。
文本主题
文本主题是确保应用内全部文本一致且可读的有效方法。例如,文本主题样式能够是黑色或白色,具体取决于主题主要颜色的亮度。这可确保文本与背景造成适当的对比,使其始终可读。
在 Flutter 文档中了解有关文本主题的更多信息。
在 _buildShrineTheme
的 errorColor 后添加如下内容:
// TODO:添加文本主题(103)
textTheme: _buildShrineTextTheme(base.textTheme),
primaryTextTheme: _buildShrineTextTheme(base.primaryTextTheme),
accentTextTheme: _buildShrineTextTheme(base.accentTextTheme),
复制代码
在点击中止按钮后再次点击容许按钮。
登录页面和主屏幕中的文本看起来有些不一样 —— 有些使用 Rubik 字体,其余文本则呈现棕色,而不是黑色或白色。
有关排版的注意事项:
- 当选择文本字体时注意,为小号和主体文本选择清晰的字体,而不是注重某种样式。
- 用做标题的、大号文本的字体应该用来表达或强调品牌。
注意到没有,咱们的图标仍然时白色的,这是由于它们有一个另外的主题。
将其添加到 _buildShrineTheme()
函数:
// TODO: 添加图标主题(103)
primaryIconTheme: base.iconTheme.copyWith(
color: kShrineBrown900
),
复制代码
单击运行按钮。
应用栏的图标变成棕色的了!
咱们的标签有点太大了。
在 home.dart
中,改变 children:
字段最内部的列:
// TODO:改变最内部的列(103)
children: <Widget>[
// TODO:处理溢出标签(103)
Text(
product == null ? '' : product.name,
style: theme.textTheme.button,
softWrap: false,
overflow: TextOverflow.ellipsis,
maxLines: 1,
),
SizedBox(height: 4.0),
Text(
product == null ? '' : formatter.format(product.price),
style: theme.textTheme.caption,
),
// 新增代码结尾
],
复制代码
咱们想要将标签居中,并将文本与每张卡片的底部,而不是图片的底部对齐。
将标签移动到主轴的结尾(底部)并将它们改成居中:
// TODO:将标签对齐底部和中心(103)
mainAxisAlignment: MainAxisAlignment.end,
crossAxisAlignment: CrossAxisAlignment.center,
复制代码
保存项目。
已经很接近了,可是文本还不是在卡片的居中位置。
更改父列的横轴对齐:
// TODO:卡片内容居中(103)
crossAxisAlignment: CrossAxisAlignment.center,
复制代码
保存项目。你的应用应该看起来像这样:
这样看起来好多了。
你也可使用 InputDecorationTheme 来主题化文本框的修饰。
在 app.dart
中的 _buildShrineTheme()
方法里,指定 inputDecorationTheme:
的值:
// TODO:修饰输入内容(103)
inputDecorationTheme: InputDecorationTheme(
border: OutlineInputBorder(),
),
复制代码
如今,文本框有一个 filled
修饰。让咱们移除它。
在 login.dart
内,移除 filled: true
值:
// 移除 filled: true 值(103)
TextField(
controller: _usernameController,
decoration: InputDecoration(
// 移除 filled: true
labelText: 'Username',
),
),
SizedBox(height: 12.0),
TextField(
controller: _passwordController,
decoration: InputDecoration(
// 移除 filled: true
labelText: 'Password',
),
obscureText: true,
),
复制代码
单击中止按钮,而后单击运行(为了从头开始启动应用程序)。你的登录页面在用户名文本框处于活动状态时(当你输入时)应该是这样的:
在正确的强调色文本框修饰和浮动占位符渲染中输入。可是咱们不能轻易地看到它。给那些没法区分足够高色彩对比度像素的人带设置了障碍。(更多详细信息,参看 Material 指南中有关“无障碍颜色”的色彩文章。)让咱们建立一个特殊类来覆盖部件的强调颜色,将其变成设计师在上面的颜色主题中为咱们提供的 PrimaryVariant。
在 login.dart
中任何其余类的范围以外添加如下内容:
// TODO:添增强调色覆盖(103)
class AccentColorOverride extends StatelessWidget {
const AccentColorOverride({Key key, this.color, this.child})
: super(key: key);
final Color color;
final Widget child;
@override
Widget build(BuildContext context) {
return Theme(
child: child,
data: Theme.of(context).copyWith(accentColor: color),
);
}
}
复制代码
下一步,将 AccentColorOverride
应用到文本框。
在 login.dart
中,导入 colors:
import 'colors.dart';
复制代码
使用新的部件包装 Username 文本框:
// TODO:使用 AccentColorOverride 包装 Username(103)
// [Name]
AccentColorOverride(
color: kShrineBrown900,
child: TextField(
controller: _usernameController,
decoration: InputDecoration(
labelText: 'Username',
),
),
),
复制代码
一样使用新的部件包装 Password 文本框:
// TODO:使用 AccentColorOverride 包装 Password(103)
// [Password]
AccentColorOverride(
color: kShrineBrown900,
child: TextField(
controller: _passwordController,
decoration: InputDecoration(
labelText: 'Password',
),
),
),
复制代码
单击运行按钮。
如今你已经为页面设置了与 Shrine 相匹配的特定颜色和排版,让咱们看看展现 Shrine 产品的卡片。这些卡片位于导航旁边的白色平面上。
在 home.dart
中为卡片添加 elevation:
值:
// TODO:调整卡片高度(103)
elevation: 0.0,
复制代码
保存你的项目。
如今你已经移除了卡片下的阴影。
让咱们更改登录页面组件的高度来补全它。
RaisedButton 的默认高度是 2。让咱们把它调高一点。
在 login.dart
中为 NEXT RaisedButton 添加 elevation:
值:
RaisedButton(
child: Text('NEXT'),
elevation: 8.0, // 新增代码
复制代码
单击中止按钮,而后单击运行。你的登录页面看起来应该是这样的:
关于高度(Elevation)的说明:
- 全部 Material Design 的平面(surface)和组件都拥有高度值。
- 一个平面末尾与另外一个平面开始的分隔由平面的边缘区分。
- 表面之间的高差可使用暗淡的或明亮的背景或阴影来表示。
- 其它平面前的平面一般包含更重要的内容。
Shrine 定义了八角形或矩形的元素,它具备酷炫的几何风格。让咱们在主屏幕上的卡片以及登陆屏幕上的文本字段和按钮中实现形状样式。
在 app.dart
中,导入 special cut corners border 文件:
import 'supplemental/cut_corners_border.dart';
复制代码
仍是在 app.dart
中,在文本字段的修饰主题上添加一个带有切角的形状:
// TODO:修饰输入内容(103)
inputDecorationTheme: InputDecorationTheme(
border: CutCornersBorder(), // 替换代码
),
复制代码
在 login.dart
中,向 CANCEL 按钮添加一个斜面矩形边框:
FlatButton(
child: Text('CANCEL'),
shape: BeveledRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(7.0)),
),
复制代码
FlatButton 没有可见的形状,为何咱们要添加边框形状?这样触摸时,波纹动画将绑定到相同的形状。
如今给 NEXT 按钮添加一样的形状:
RaisedButton(
child: Text('NEXT'),
elevation: 8.0,
shape: BeveledRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(7.0)),
),
复制代码
关于形状的说明:
- 使用形状能够促进品牌的视觉表达。
- 形状具备可调曲线和无角度拐角,曲线和边角以及拐角总数。
- 组件的形状不该该干扰其可用性!
单击中止按钮,而后单击运行:
接下来,让咱们更改布局以显示不一样宽高比和大小的卡片,以便使每张卡片看起来都是不一样的。
咱们已经为不对称的布局编写了文件。
在 home.dart
中,修改如下全部文件:
import 'package:flutter/material.dart';
import 'model/products_repository.dart';
import 'model/product.dart';
import 'supplemental/asymmetric_view.dart';
class HomePage extends StatelessWidget {
// TODO:为 Category 添加变量(104)
@override
Widget build(BuildContext context) {
// TODO:返回一个 AsymmetricView(104)
// TODO:传递 Category 变量给 AsymmetricView(104)
return Scaffold(
appBar: AppBar(
brightness: Brightness.light,
leading: IconButton(
icon: Icon(Icons.menu),
onPressed: () {
print('Menu button');
},
),
title: Text('SHRINE'),
actions: <Widget>[
IconButton(
icon: Icon(Icons.search),
onPressed: () {
print('Search button');
},
),
IconButton(
icon: Icon(Icons.tune),
onPressed: () {
print('Filter button');
},
),
],
),
body: AsymmetricView(products: ProductsRepository.loadProducts(Category.all)),
);
}
}
复制代码
保存项目。
如今产品以编织图案风格水平滚动。此外状态栏文本(顶部的时间和网络)如今为黑色。那是由于咱们将 AppBar 的 brightness 改成了 light,brightness: Brightness.light
颜色是诠释品牌的有效方式,颜色的微小变化会对您的用户体验产生很大影响。为了测试这一点,让咱们看看若是品牌的配色方案彻底不一样时 Shrine 会是什么样子。
在 colors.dart
中,添加如下内容:
const kShrineAltDarkGrey = const Color(0xFF414149);
const kShrineAltYellow = const Color(0xFFFFCF44);
复制代码
在 app.dart
中,按照如下内容修改 _buildShrineTheme()
和 _buildShrineTextTheme
方法:
ThemeData _buildShrineTheme() {
final ThemeData base = ThemeData.dark();
return base.copyWith(
accentColor: kShrineAltDarkGrey,
primaryColor: kShrineAltDarkGrey,
buttonColor: kShrineAltYellow,
scaffoldBackgroundColor: kShrineAltDarkGrey,
cardColor: kShrineAltDarkGrey,
textSelectionColor: kShrinePink100,
errorColor: kShrineErrorRed,
textTheme: _buildShrineTextTheme(base.textTheme),
primaryTextTheme: _buildShrineTextTheme(base.primaryTextTheme),
accentTextTheme: _buildShrineTextTheme(base.accentTextTheme),
primaryIconTheme: base.iconTheme.copyWith(
color: kShrineAltYellow
),
inputDecorationTheme: InputDecorationTheme(
border: CutCornersBorder(),
),
);
}
TextTheme _buildShrineTextTheme(TextTheme base) {
return base.copyWith(
headline: base.headline.copyWith(
fontWeight: FontWeight.w500,
),
title: base.title.copyWith(
fontSize: 18.0
),
caption: base.caption.copyWith(
fontWeight: FontWeight.w400,
fontSize: 14.0,
),
).apply(
fontFamily: 'Rubik',
displayColor: kShrineSurfaceWhite,
bodyColor: kShrineSurfaceWhite,
);
}
复制代码
在 login.dart
中,将钻石标志变成白色:
Image.asset(
'assets/diamond.png',
color: kShrineBackgroundWhite, // 新增代码
),
复制代码
仍是在 login.dart
中,将两个文本字段的强调色覆盖更改成黄色:
AccentColorOverride(
color: kShrineAltYellow, // 修改的代码
child: TextField(
controller: _usernameController,
decoration: InputDecoration(
labelText: 'Username',
),
),
),
SizedBox(height: 12.0),
AccentColorOverride(
color: kShrineAltYellow, // 修改的代码
child: TextField(
controller: _passwordController,
decoration: const InputDecoration(
labelText: 'Password',
),
),
),
复制代码
在 home.dart
中,修改 brightness 为 dark:
brightness: Brightness.dark,
复制代码
保存项目。如今应该出现新的主题了。
结果很是不一样!让咱们在转到 104 教程以前还原这个颜色代码。
到目前为止,您已经建立了一个按照设计师设计规范设计的应用程序。
完整的 MDC-103 应用程序可在
104-starter_and_102-complete
分支中找到。您能够针对该分支中的应用测试您的页面版本。
你如今已经使用过了如下 MDC 组件:主题、排版、高度和形状。你能够在 MDC-Flutter 库中探索更多组件和子系统。
深刻 supplemental
目录中的文件来了解咱们是如何制做水平滚动的,非对称的布局网格的。
若是您的应用程序设计包含 MDC 库中没有的组件元素该怎么办?在 MDC-104: Material Design 高级组件一文中咱们将展现如何使用 MDC 库建立自定义组件以实现特定外观。
若是发现译文存在错误或其余须要改进的地方,欢迎到 掘金翻译计划 对译文进行修改并 PR,也可得到相应奖励积分。文章开头的 本文永久连接 即为本文在 GitHub 上的 MarkDown 连接。
掘金翻译计划 是一个翻译优质互联网技术文章的社区,文章来源为 掘金 上的英文分享文章。内容覆盖 Android、iOS、前端、后端、区块链、产品、设计、人工智能等领域,想要查看更多优质译文请持续关注 掘金翻译计划、官方微博、知乎专栏。