1. 簡介
Material 元件 (MDC) 可協助開發人員實作 Material Design。MDC 是由 Google 工程師和使用者體驗設計師團隊打造,提供數十種精美且實用的 UI 元件,適用於 Android、iOS、網頁和 Flutter.material.io/develop |
在 MDC-101 程式碼研究室中,您使用了兩個 Material 元件來建立登入頁面:文字欄位和帶有墨水波紋的按鈕。接下來,我們將在這個基礎上加入導覽、結構和資料。
建構項目
在本程式碼研究室中,您會為名為 Shrine 的電子商務應用程式建構主畫面,這是一款用於販售服飾和居家用品的電子商務應用程式。其中包含:
- 頂端應用程式列
- 完整的產品格狀清單
Android | iOS |
本程式碼研究室中的 Material Flutter 元件和子系統
- 頂端應用程式列
- 格線
- 資訊卡
針對 Flutter 開發經驗,您會給予什麼評價?
2. 設定 Flutter 開發環境
您需要兩個軟體才能完成這個實驗室活動,分別是 Flutter SDK 和編輯器。
您可以使用下列任一裝置執行程式碼研究室:
- 連接至電腦的實體 Android 或 iOS 裝置,並設為開發人員模式。
- iOS 模擬器 (需要安裝 Xcode 工具)。
- Android Emulator (需要在 Android Studio 中設定)。
- 瀏覽器 (偵錯時必須使用 Chrome)。
- 下載 Windows、Linux 或 macOS 桌面應用程式。您必須在預計部署的平台上進行開發。因此,如果您想要開發 Windows 電腦版應用程式,就必須在 Windows 上進行開發,以便存取適當的建構鏈結。如要進一步瞭解作業系統的特定需求,請參閱 docs.flutter.dev/desktop。
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/mdc_100_series git checkout 102-starter_and_101-complete
開啟專案並執行應用程式
- 在您選擇的編輯器中開啟專案。
- 按照所選編輯器的「開始使用:測試」部分,按照「執行應用程式」的說明操作。
大功告成!您應該會在裝置上的 MDC-101 程式碼研究室中看到 Shrine 登入頁面。
Android | iOS |
登入畫面看起來不錯,現在讓我們在應用程式中填入一些產品。
4. 新增頂端應用程式列
目前,如果您按一下「Next」按鈕,就會看到主畫面顯示「You did it!」(您成功了!)。太好了!但使用者現在不需要採取任何行動,或全盤知道自己在應用程式中的哪個位置。為了提供協助,請開始新增導覽功能。
Material Design 提供的瀏覽模式可確保高度可用性。最常見的元件之一就是頂端應用程式列。
如要提供導覽選項,並讓使用者快速存取其他動作,請加入頂端應用程式列。
新增 AppBar 小工具
在 home.dart
中,將 AppBar 新增至 Scaffold,並移除醒目顯示的 const
:
return const Scaffold(
// TODO: Add app bar (102)
appBar: AppBar(
// TODO: Add buttons and title (102)
),
將 AppBar 新增至 Scaffold 的 appBar:
欄位,即可免費獲得完美的版面配置,讓 AppBar 位於頁面頂端,而內文位於下方。
新增文字小工具
在 home.dart
中,為 AppBar 新增標題:
// TODO: Add app bar (102)
appBar: AppBar(
// TODO: Add buttons and title (102)
title: const Text('SHRINE'),
// TODO: Add trailing buttons (102)
儲存專案。
Android | iOS |
許多應用程式列的標題旁邊都有按鈕。我們要在應用程式中新增選單圖示。
新增前置 IconButton
在 home.dart
中,為 AppBar 的 leading:
欄位設定 IconButton。(請將其放在 title:
欄位前,模擬從前到後的順序):
// TODO: Add buttons and title (102)
leading: IconButton(
icon: const Icon(
Icons.menu,
semanticLabel: 'menu',
),
onPressed: () {
print('Menu button');
},
),
儲存專案。
Android | iOS |
選單圖示 (又稱為「漢堡」) 會顯示在所需位置。
您也可以在標題後方加入按鈕。在 Flutter 中,這些稱為「動作」。
新增動作
剩下 2 個 IconButton。
將這些元素加入 AppBar 例項的標題後方:
// TODO: Add trailing buttons (102)
actions: <Widget>[
IconButton(
icon: const Icon(
Icons.search,
semanticLabel: 'search',
),
onPressed: () {
print('Search button');
},
),
IconButton(
icon: const Icon(
Icons.tune,
semanticLabel: 'filter',
),
onPressed: () {
print('Filter button');
},
),
],
儲存專案。主畫面應如下所示:
Android | iOS |
現在應用程式右側有一個前置按鈕、標題,以及兩個動作。應用程式列也可以使用細微的陰影顯示「高度」,指出所在位置與內容位於不同層。
5. 在格狀中新增資訊卡
應用程式有了一些結構後,我們現在要將內容放入資訊卡來進行整理。
新增 GridView
讓我們先在頂端應用程式列下方新增一張資訊卡。單獨的 Card 小工具沒有足夠的資訊,無法在我們可見的位置進行版面配置,因此我們會將其封裝在 GridView 小工具中。
將 Scaffold 內文中的中心替換為 GridView:
// TODO: Add a grid view (102)
body: GridView.count(
crossAxisCount: 2,
padding: const EdgeInsets.all(16.0),
childAspectRatio: 8.0 / 9.0,
// TODO: Build a grid of cards (102)
children: <Widget>[Card()],
),
讓我們來解壓縮這段程式碼。GridView 會叫用 count()
建構函式,因為其顯示的項目數量可以計數,而非無限量。但需要更多資訊才能定義其版面配置。
crossAxisCount:
會指定其中的項目數量。您想要 2 欄。
padding:
欄位會在 GridView 的全部 4 邊提供空間。當然,您看不到結尾或底部的邊框間距,因為它們旁邊還沒有 GridView 子項。
childAspectRatio:
欄位會根據顯示比例 (寬度大於高度) 識別項目大小。
根據預設,GridView 會讓所有大小相同的圖塊。
我們有張卡片,但沒有任何內容。讓我們在資訊卡中新增子項小工具。
版面配置內容
資訊卡應包含圖片、標題和次要文字的區域。
更新 GridView 的子項:
// TODO: Build a grid of cards (102)
children: <Widget>[
Card(
clipBehavior: Clip.antiAlias,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
AspectRatio(
aspectRatio: 18.0 / 11.0,
child: Image.asset('assets/diamond.png'),
),
Padding(
padding: const EdgeInsets.fromLTRB(16.0, 12.0, 16.0, 8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text('Title'),
const SizedBox(height: 8.0),
Text('Secondary Text'),
],
),
),
],
),
)
],
此程式碼會新增「欄」小工具,用來垂直排列子項小工具。
crossAxisAlignment: field
指定 CrossAxisAlignment.start
,表示「將文字對齊前緣」。
無論您提供何種圖片,AspectRatio 小工具都會決定圖片的形狀。
「邊框間距」能從側邊產生文字。
這兩個「Text」小工具彼此垂直堆疊,彼此之間留有 8 個空白處 (SizedBox)。我們建立另一個 Column,用來存放邊框間距。
儲存專案。
Android | iOS |
在這個預覽畫面中,您可以看到資訊卡從邊緣插入到邊緣,並有圓角和陰影 (代表卡片高度)。整個形狀在 Material 中稱為「容器」。(請勿與名為 Container 的實際小工具類別混淆)。
資訊卡通常會與其他資訊卡一同顯示在集合中。讓我們將這些圖片以集合形式排列在格狀檢視畫面中。
6. 建立資訊卡集
當畫面顯示多張資訊卡時,系統會將這些資訊卡歸入一或多個集合。集合中的資訊卡是共平面,也就是說,這些資訊卡會共用相同的靜止高度 (除非資訊卡被提起或拖曳,但我們不會在這裡這樣做)。
將資訊卡乘以集合
目前資訊卡是透過 GridView 的 children:
欄位內嵌建構。這會產生大量難以閱讀的巢狀程式碼。讓我們將其擷取至函式,以便產生任意數量的空白資訊卡,並傳回資訊卡清單。
在 build()
函式上方建立新的私人函式 (請注意,以底線開頭的函式為不公開 API):
// TODO: Make a collection of cards (102)
List<Card> _buildGridCards(int count) {
List<Card> cards = List.generate(
count,
(int index) {
return Card(
clipBehavior: Clip.antiAlias,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
AspectRatio(
aspectRatio: 18.0 / 11.0,
child: Image.asset('assets/diamond.png'),
),
Padding(
padding: const EdgeInsets.fromLTRB(16.0, 12.0, 16.0, 8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: const <Widget>[
Text('Title'),
SizedBox(height: 8.0),
Text('Secondary Text'),
],
),
),
],
),
);
},
);
return cards;
}
將產生的資訊卡指派給 GridView 的 children
欄位。請記得用這個新程式碼取代 GridView 中的所有內容:
// TODO: Add a grid view (102)
body: GridView.count(
crossAxisCount: 2,
padding: const EdgeInsets.all(16.0),
childAspectRatio: 8.0 / 9.0,
children: _buildGridCards(10) // Replace
),
儲存專案。
Android | iOS |
卡片已顯示,但還沒有任何內容。接著,請新增產品資料。
新增產品資料
這個應用程式中有部分產品附有圖片、名稱和價格。讓我們將這項資訊加入目前已在資訊卡中顯示的小工具
然後在 home.dart
中,匯入新的套件和我們為資料模型提供的一些檔案:
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'model/product.dart';
import 'model/products_repository.dart';
最後,變更 _buildGridCards()
以擷取產品資訊,並在資訊卡中使用該資料:
// TODO: Make a collection of cards (102)
// Replace this entire method
List<Card> _buildGridCards(BuildContext context) {
List<Product> products = ProductsRepository.loadProducts(Category.all);
if (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(
clipBehavior: Clip.antiAlias,
// TODO: Adjust card heights (103)
child: Column(
// TODO: Center items on the card (103)
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
AspectRatio(
aspectRatio: 18 / 11,
child: Image.asset(
product.assetName,
package: product.assetPackage,
// TODO: Adjust the box size (102)
),
),
Expanded(
child: Padding(
padding: const EdgeInsets.fromLTRB(16.0, 12.0, 16.0, 8.0),
child: Column(
// TODO: Align labels to the bottom and center (103)
crossAxisAlignment: CrossAxisAlignment.start,
// TODO: Change innermost Column (103)
children: <Widget>[
// TODO: Handle overflowing labels (103)
Text(
product.name,
style: theme.textTheme.titleLarge,
maxLines: 1,
),
const SizedBox(height: 8.0),
Text(
formatter.format(product.price),
style: theme.textTheme.titleSmall,
),
],
),
),
),
],
),
);
}).toList();
}
注意:不會編譯及執行。我們還有一項變更
此外,在嘗試編譯之前,變更 build()
函式,將 BuildContext 傳遞至 _buildGridCards()
:
// TODO: Add a grid view (102)
body: GridView.count(
crossAxisCount: 2,
padding: const EdgeInsets.all(16.0),
childAspectRatio: 8.0 / 9.0,
children: _buildGridCards(context) // Changed code
),
熱啟動應用程式。
Android | iOS |
您可能會注意到,我們沒有在資訊卡之間加入任何垂直間距。這是因為這類廣告的頂端和底部設有 4 個邊界。
儲存專案。
產品資料會顯示,但圖片周圍會多出額外的空間。根據預設,圖片會以 .scaleDown
的 BoxFit 繪製。我們將其變更為 .fitWidth
,讓圖片稍微放大並移除多餘的空白空間。
將 fit:
欄位新增至圖片,並將值設為 BoxFit.fitWidth
:
// TODO: Adjust the box size (102)
fit: BoxFit.fitWidth,
Android | iOS |
我們的產品現在能完美顯示在應用程式中!
7. 恭喜!
我們的應用程式具備基本流程,能將使用者導向登入畫面的主畫面,供使用者查看產品。在幾行程式碼中,我們加入了頂端應用程式列 (含標題和三個按鈕) 和資訊卡 (用來呈現應用程式的內容)。我們的首頁現在簡單實用,具備基本結構和可執行內容。
後續步驟
透過頂端應用程式列、資訊卡、文字欄位和按鈕,我們現已使用 Material Flutter 程式庫中的四個核心元件!詳情請參閱 Material 元件小工具目錄。
雖然應用程式已完全運作,但尚未表達任何特定品牌或觀點。在 MDC-103:Material Design 主題設定搭配顏色、形狀、高度和類型,我們將自訂這些元件的樣式,呈現一種鮮豔的現代品牌。