[譯] MDC-102 Flutter:Material 結構和佈局(Flutter)

MDC-102 Flutter:Material 結構和佈局(Flutter)

1. 介紹

Material Components(MDC)幫助開發者實現 Material Design。MDC 由谷歌團隊的工程師和 UX 設計師創造,爲 Android、iOS、Web 和 Flutter 提供不少美觀實用的 UI 組件。

material.io/develophtml

在教程 MDC-101 中,你使用了兩個 Material 組件:文本框和墨水波紋效果的按鈕來構建一個登錄頁面。如今讓咱們經過添加導航、結構和數據來拓展應用。前端

你將要構建

在本教程中,你將爲 Shrine —— 一個銷售服裝和家居用品的電子商務應用程序構建一個主頁面。它將含有:android

  • 一個位於頂部的應用欄
  • 一個由產品填充的網格列表

這是四篇教程裏的第二篇,它將引導你爲 Shrine 的產品構建應用程序。咱們建議你按照教程的順序一步一步地編寫你的代碼。ios

相關的教程能夠在如下位置找到:git

到 MDC-104 的最後,你將會構建一個像這樣的應用:github

將要用到的 MDC 組件

  • 頂部應用欄(Top app bar)
  • 網格(Grid)
  • 卡片(Card)

本教程中,你將使用 MDC-Flutter 提供的默認組件。你將會在 MDC-103: Material Design Theming 的顏色、形狀、高度和類型中學習如何定製它們。後端

你將須要

  • Flutter SDK
  • 安裝好 Flutter 插件的 Android Studio,或者你喜歡的代碼編輯器
  • 示例代碼

要在 iOS 上構建和運行 Flutter 應用程序,你須要知足如下要求:

  • 運行 macOS 的計算機
  • Xcode 9 或更新版本
  • iOS 模擬器,或者 iOS 物理設備

要在 Android 上構建和運行 Flutter 應用程序,你須要知足如下要求:

  • 運行 macOS、Windows 或 Linux 的計算機
  • Android Studio
  • Android 模擬器(隨 Android Studio 一塊兒提供)或 Android 物理設備

2. 安裝 Flutter 環境

前提條件

要開始使用 Flutter 開發移動應用程序,你須要:bash

  • Flutter SDK
  • 裝有 Flutter 插件的 IntelliJ IDE,或者你喜歡的代碼編輯器

Flutter 的 IDE 工具適用於 Android StudioIntelliJ IDEA Community(免費)和 IntelliJ IDEA Ultimateapp

要在 iOS 上構建和運行 Flutter 應用程序,你須要知足如下要求:

  • 運行 macOS 的計算機
  • Xcode 9 或更新版本
  • iOS 模擬器,或者 iOS 物理設備

要在 Android 上構建和運行 Flutter 應用程序,你須要知足如下要求:

  • 運行 macOS、Windows 或者 Linux 的計算機
  • Android Studio
  • Android 模擬器(隨 Android Studio 一塊兒提供)或 Android 物理設備

獲取詳細的 Flutter 安裝信息編輯器

重要提示:若是鏈接到計算機的 Android 手機上出現「容許 USB 調試」對話框,請啓用始終容許今後計算機選項,而後單擊肯定

在繼續本教程以前,請確保你的 SDK 處於正確的狀態。若是以前安裝過 Flutter,則使用 flutter upgrade 來確保 SDK 處於最新版本。

flutter upgrade
複製代碼

運行 flutter upgrade 將自動運行 flutter doctor。若是這是首次安裝 Flutter 且不需升級,那麼請手動運行 flutter doctor。查看顯示的全部檢查標記;這將會下載你須要的任何缺乏的 SDK 文件,並確保你的計算機配置無誤以進行 Flutter 的開發。

flutter doctor
複製代碼

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: <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 文檔中瞭解更多有關的信息。

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: <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 指南上有關卡片的文章。

卡片常常以集合的形式和其餘卡片一塊兒出現,讓咱們在網格視圖中給它們佈局。

6. 生成卡片集合

每當屏幕上出現多張卡片時,它們就會組成一個或多個集合。集合中的卡片是共面的,這意味着卡片共享相同的靜止高度。(除了卡片被拾起或拖動,但在這裏咱們不會這麼作。)

將卡片添加到集合

如今咱們的卡片是網格視圖內的 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 個單位的填充。

保存你的項目:

產品的數據顯示出來了,可是圖像四周有額外的空間。圖像默認依據 .scaleDownBoxFit 繪製(在這個狀況下)。讓咱們將其更改成 .fitWidth 來讓它們放大一點,刪除多餘的空間。

修改圖像的 fit: 字段:

// TODO: 調整盒子尺寸(102)
  fit: BoxFit.fitWidth,
複製代碼

如今咱們的產品完美的展示在應用中了!

7. 總結

咱們的應用已經有了基本的流程,將用戶從登錄屏幕帶到能夠查看產品的主屏幕。經過幾行代碼,咱們添加了一個頂部應用欄(帶有標題和三個按鈕)以及卡片(用於顯示咱們應用的內容)。咱們的主屏幕簡潔實用,具備基本的結構和可操做的內容。

完成的 MDC-102 應用能夠在 103-starter_and_102-complete 分支中找到。

你能夠用此分支下的應用來對照驗證你的版本。

下一步

經過頂部應用欄、卡片、文本框和按鈕,咱們已經使用了 MDC-Flutter 庫中的四個核心組件!你能夠訪問 Flutter 部件目錄來探索更多組件。

雖然它徹底正常運行,咱們的應用還沒有表達任何特殊的品牌特色。在 MDC-103: Material Design Theming 的顏色、形狀、高度和類型中,咱們將定製這些組件的樣式,來詮釋一個充滿活力的、現代的品牌。

若是發現譯文存在錯誤或其餘須要改進的地方,歡迎到 掘金翻譯計劃 對譯文進行修改並 PR,也可得到相應獎勵積分。文章開頭的 本文永久連接 即爲本文在 GitHub 上的 MarkDown 連接。


掘金翻譯計劃 是一個翻譯優質互聯網技術文章的社區,文章來源爲 掘金 上的英文分享文章。內容覆蓋 AndroidiOS前端後端區塊鏈產品設計人工智能等領域,想要查看更多優質譯文請持續關注 掘金翻譯計劃官方微博知乎專欄

相關文章
相關標籤/搜索