[譯]在 Flutter 中如何設計 Activity 介面?

趙阿海發表於2019-09-06

這篇部落格是為那些想把現有的開發知識應用 Flutter 的 Android 開發者寫的。在這篇文章裡,我們將會探索在 Flutter 裡與 Activity 對應的是什麼。

部落格系列

先決條件

這篇文章假設你已經在電腦上配置好了 Flutter 的執行環境並且能成功執行一個 Hello World 專案。如果你還沒有安裝 Flutter,你可以從這裡開始

Dart 是一門基於物件導向的語言,對一個 Android 開發者來說是很容易掌握的。

目標

在這篇文章結束時,我們將使用 Flutter 建立一個如下圖所示的 Activity 佈局。

Goal ui

技術上來說,如果你研究過 Flutter 生成的 Android 專案並且檢視過它的 AndroidMenifest.xml 檔案,你會發現它只執行了一個 Activity,比如 FlutterActivity,但這篇文章要探討的問題是,在 Flutter 裡如何設計一個 Activity 的介面?
答案是...Scaffold

Scaffold

Scaffold 是一個表示 Activity 介面的小部件。作為 Android 開發者,我們使用 Activity 來表示一屏內容,它可以包括頂部工具欄(Toolbar)、選單(Menus)、側滑選單(Drawer)、底部導航欄(BottomNavigationBar)、底部提示(SnackBar)、懸浮按鈕(FloatActionButton)等,我們還會用一個FrameLayout作為Fragment的容器。
Scaffold 以部件(Widgets)的形式包含了上述所有內容。

記住,在 Flutter 裡,一切皆部件(Widget)。

下面這張圖片展示了 Scaffold 的內容組成:它提供了用於展示左右兩側佈局的 API,即DrawerLayout;BottomBar 就是 Android 中的 BottomNavigationView,App bar 就是 Toolbar,我們可以把 Content area 當做上面說的FrameLayout容器。

Scaffold

由於 Scaffold 是材料部件(Material Widgets)的一部分,因此它需要一個材料App(MaterialApp)作為父容器。
我們將會在接下來的文章探討關於 MaterialApp 的細節,現在我們先來看看如何建立一個 Scaffold 部件。

import 'package:flutter/material.dart';

void main() => runApp(MaterialApp(
      home: Scaffold(
      ),
    ));
複製程式碼

執行上面的程式碼,你將會看到一個白色的介面,因為我們沒有在 Scaffold 裡面新增任何東西,讓我們通過backgroundColor屬性來設定一個黃色的背景:

import 'package:flutter/material.dart';

void main() =>
    runApp(MaterialApp(
      home: Scaffold(
        //設定背景色為黃色  
        backgroundColor: Colors.yellowAccent,
      ),
    ));
複製程式碼

執行程式碼後你就能看到螢幕上出現黃色的背景了。

你可以試著設定其他屬性並通過熱過載(Hot Reload)來檢視執行結果,你也可以在官方文件檢視Scaffold的所有屬性。

現在你知道如何建立一個 Scaffold 了,我們接下來將會一個一個的探索它的主要屬性。

1.Appbar(Toolbar)

Appbar 展示的部件和我們在 Activity 中使用的 Toolbar 是相同的,下面的圖展示了在所用的語言方向是從左到右(例如英語)時,Appbar 的每個屬性出現在工具欄的對應位置。

Appbar

  • leading:展示在標題之前的控制元件,這個控制元件通常用來展示圖示或者後退按鈕。
  • title:用一個 Text 控制元件來包裹 Toolbar 的標題。
  • actions:這和我們使用 menu.xml 通過定義<item/>來展示選單是一樣的,actions 屬性接收一個控制元件列表來在 Appbar 上展示選單,這些控制元件通常是 IconButton
  • bottom:bottom 通常用於在 Appbar 下面展示一個 TabBar
  • flexibleSpace: 這個部件通常用於配合 Appbar 建立 CollapsingToolbarLayout 效果。

你可以像下面這樣建立一個包含leading、title 和 menus 的 Appbar:

import 'package:flutter/material.dart';

void main() =>
    runApp(MaterialApp(
      home: Scaffold(
        backgroundColor: Colors.yellowAccent,
        appBar: AppBar(
          leading: Icon(Icons.menu),
          title: Text('My Title'),
          actions: <Widget>[
            IconButton(
              color: Colors.white,
              icon: Icon(
                Icons.shopping_cart,
                color: Colors.white,
              ),
              onPressed: null,
            ),
            IconButton(
              icon: Icon(
                Icons.monetization_on,
                color: Colors.white,
              ),
              onPressed: null,
            )
          ],
        ),
      ),
    ));
複製程式碼

下圖就是上面程式碼的執行效果,它看起來和 Activity 的 Toolbar非常像。

ui-appbar

你可以試著增刪控制元件,或者為控制元件提供一個樣式或顏色,也可以把探索 Appbar 的其他屬性作為一個練習。

2.Body (其他 View 的容器)

這是 Scaffold 的主要內容區域,可以充當 Fragment 的容器。它接收一個控制元件並把它展示出來,這就是我們向使用者展示主要內容的地方。
為了簡單起見,在這個例子中我們只為 body 新增一個紅色背景。在實際使用中,可不止一個背景顏色這麼簡單,還可以新增 ListViewRowColumnStack等。

import 'package:flutter/material.dart';

void main() =>
    runApp(MaterialApp(
      home: Scaffold(
        backgroundColor: Colors.yellowAccent,
        appBar: AppBar(
          leading: Icon(Icons.menu),
          title: Text('My Title'),
          actions: <Widget>[
            IconButton(
              color: Colors.white,
              icon: Icon(
                Icons.shopping_cart,
                color: Colors.white,
              ),
              onPressed: null,
            ),
            IconButton(
              icon: Icon(
                Icons.monetization_on,
                color: Colors.white,
              ),
              onPressed: null,
            )
          ],
        ),
        body: Container(
          //設定紅色背景  
          color: Colors.red,
        ),
      ),
    ));
複製程式碼

ui-red

Body 的屬性展示在 Appbar 下方,並且在 floatingActionButton 和 drawer 後面。雖然我們之前給 Scaffold 定義了一個黃色的背景,但是紅色背景會覆蓋它。

3. Drawer (DrawerLayout)

這個控制元件代表 Android 中的 DrawerLayout,它可以從 Activity 邊緣水平滑入,來展示應用的導航。

drawer

Drawer 通常與 Scaffold.drawer屬性一起使用。就像在 Android 裡我們用NavigationView 填充 DrawerLayout一樣,下面的表格展示了在 Android 和 Flutter 中可以用於 Drawer 的等價控制元件。

NavigationView(Android) Drawer(Flutter)
app:headerLayout="@layout/nav_header" DrawerHeader
app:menu="@menu/drawer_view" ListTile
android:layout_gravity="start" Use Scaffold.drawer property
android:layout_gravity="end" Use Scaffold.endDrawer property

Drawer 的子控制元件通常是一個ListView,它的第一個子控制元件是DrawerHeader,用來展示當前使用者的狀態資訊。Drawer的其他子控制元件通常使用ListTile來構造。

下面的程式碼展示瞭如何建立一個Drawer:

import 'package:flutter/material.dart';

void main() =>
    runApp(MaterialApp(
      home: Scaffold(
        backgroundColor: Colors.yellowAccent,
        appBar: AppBar(
          // leading: Icon(Icons.menu),  
          title: Text('My Title'),
          actions: <Widget>[
            IconButton(
              color: Colors.white,
              icon: Icon(
                Icons.shopping_cart,
                color: Colors.white,
              ),
              onPressed: null,
            ),
            IconButton(
              icon: Icon(
                Icons.monetization_on,
                color: Colors.white,
              ),
              onPressed: null,
            )
          ],
        ),
        body: Container(
          color: Colors.red,
        ),
        drawer: Drawer(
          child: ListView(
            children: <Widget>[
              DrawerHeader(
                child: Text("Drawer header"),
                decoration: BoxDecoration(
                  color: Colors.blue,
                ),
              ),
              Text("Item 1"),
              Text("Item 2"),
              Text("Item 3"),
              Text("Item 4"),
              Text("Item 5"),
              Text("Item 6"),
            ],
          ),
        ),
      ),
    ));
複製程式碼

上面程式碼的執行結果如下圖所示:

ui-drawer

需要注意一點,上面的程式碼中我們去掉了 appBar 的 leading 圖示,當我們給 Scaffold 新增 drawer 時,它會自動在 appbar 的 leading 上新增一個漢堡圖示。

關於 drawer 的更多細節可以檢視下面的連結:

3.BottomNavigationBar (BottomNavigationView)

BottomNavigationBar 是一個展示在應用底部,用於選擇不同檢視(通常3-5個)的材料控制元件。底部導航欄包括很多項,可以是浮在材料佈局上的文字、圖示或者兩者都有。

底部導航欄通常和 Scaffold 一起使用,可以通過 Scaffold.bottomNavigationBar 屬性提供。

在 Android 裡,你通過app:menu=”@menu/my_navigation_items”來定義 BottomNavigationView的選單項。my_navigation_items 包括所有選單項的<item/>標籤列表。

在 Flutter 裡,我們使用 items 屬性,它接收一個 BottomNavigationBarItem 列表。BottomNavigationBarItem 包含選單的圖示、標題和背景色。

import 'package:flutter/material.dart';

void main() =>
    runApp(MaterialApp(
      home: Scaffold(
        backgroundColor: Colors.yellowAccent,
        appBar: ....,
        body:...,
        drawer:...,
        bottomNavigationBar: BottomNavigationBar(
            items: [
              BottomNavigationBarItem(
                icon: Icon(Icons.home),
                title: Text("Home"),
              ),
              BottomNavigationBarItem(
                icon: Icon(Icons.search),
                title: Text("Search"),
              )
            ]
        ),
      ),
    ));
複製程式碼

[譯]在 Flutter 中如何設計 Activity 介面?

如你所見,底部有一個包含兩個選單的 BottomNavigationBar

處理點選事件和改變 Scaffold 的 body 屬性需要一個帶狀態的部件(Stateful widget)和一些額外的工作,已經超出了這篇文章的範疇,你可以在官方文件裡瞭解更多。

此外,我還給 Scaffold 新增了一個懸浮按鈕。

下面是使用 Scaffold 展示我們Activity 介面的完整程式碼。

import 'package:flutter/material.dart';

void main() =>
    runApp(MaterialApp(
      home: Scaffold(
        backgroundColor: Colors.yellowAccent,
        appBar: AppBar(
          leading: Icon(Icons.menu),
          title: Text('My Title'),
          actions: <Widget>[
            IconButton(
              color: Colors.white,
              icon: Icon(
                Icons.shopping_cart,
                color: Colors.white,
              ),
              onPressed: null,
            ),
            IconButton(
              icon: Icon(
                Icons.monetization_on,
                color: Colors.white,
              ),
              onPressed: null,
            )
          ],
        ),
        body: Container(
          color: Colors.red,
        ),
        drawer: Drawer(
          child: ListView(
            children: <Widget>[
              DrawerHeader(
                child: Text("Drawer header"),
                decoration: BoxDecoration(
                  color: Colors.blue,
                ),
              ),
              Text("Item 1"),
              Text("Item 2"),
              Text("Item 3"),
              Text("Item 4"),
              Text("Item 5"),
              Text("Item 6"),
            ],
          ),
        ),
        bottomNavigationBar: BottomNavigationBar(
            items: [
              BottomNavigationBarItem(
                icon: Icon(Icons.home),
                title: Text("Home"),
              ),
              BottomNavigationBarItem(
                icon: Icon(Icons.search),
                title: Text("Search"),
              )
            ]
        ),
        floatingActionButton: FloatingActionButton(
          onPressed: () {},
          child: Icon(Icons.add),
        ),
      ),
    ));
複製程式碼

floating_aciton_button

如果懸浮按鈕的onPressed回撥是null,那麼按鈕將會不可用並且不響應觸控事件。所以為了有觸控效果,你需要處理onPressed回撥——保持空函式或者執行其他操作。

最終,我們完成了在文章開始要構建的介面。

總結

Flutter 是一個可以快速構建高質量、美觀介面的強大工具,它有很多部件可以用於構建具有很棒動畫的靈活介面,Scaffold 是其中的一個並且它只是冰山一角。
我希望在接下來的部落格中能探討關於它們的更多話題。

感謝!

相關文章