- 原文地址: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
要在 iOS 上構建和運行 Flutter 應用程序,你須要知足如下要求:重要提示:若是鏈接到計算機的 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、前端、後端、區塊鏈、產品、設計、人工智能等領域,想要查看更多優質譯文請持續關注 掘金翻譯計劃、官方微博、知乎專欄。