跳至主要內容

自動平台調整

適應性設計理念

#

一般而言,平台適應性存在兩種情況

  1. 屬於作業系統環境行為 (例如文字編輯和滾動),如果發生不同行為就會「錯誤」的情況。
  2. 通常在使用 OEM 的 SDK 的應用程式中實現的事物 (例如在 iOS 上使用並排標籤或在 Android 上顯示 android.app.AlertDialog)。

本文主要涵蓋 Flutter 在 Android 和 iOS 上針對第一種情況提供的自動適應。

對於第二種情況,Flutter 捆綁了產生平台慣例的適當效果的方法,但在需要應用程式設計選擇時不會自動適應。如需討論,請參閱 issue #8410Material/Cupertino 自適應 Widget 問題定義

如需在 Android 和 iOS 上使用不同資訊架構結構,但共用相同內容程式碼的應用程式範例,請參閱 platform_design 程式碼範例

#

Flutter 提供 Android 和 iOS 上可見的導覽模式,並且會自動將導覽動畫調整為目前的平台。

#

Android 上,預設的 Navigator.push() 轉場動畫是以 startActivity() 為模型,通常具有一個由下往上的動畫變體。

iOS

  • 預設的 Navigator.push() API 會產生 iOS 顯示/推播樣式轉場動畫,該動畫會根據地區設定的 RTL 設定,從結束到開始進行動畫。新路由後面的頁面也會以與 iOS 中相同的方向進行視差滑動。
  • 當推播頁面路由且 PageRoute.fullscreenDialog 為 true 時,會存在一個單獨的由下往上轉場樣式。這表示 iOS 的顯示/強制回應樣式轉場動畫,通常用於全螢幕強制回應頁面。
An animation of the bottom-up page transition on Android
Android 頁面轉場動畫
An animation of the end-start style push page transition on iOS
iOS 推播轉場動畫
An animation of the bottom-up style present page transition on iOS
iOS 顯示轉場動畫

平台特定轉場細節

#

Android 上,Flutter 使用 ZoomPageTransitionsBuilder 動畫。當使用者點擊某個項目時,UI 會縮放至以該項目為特色的畫面。當使用者點擊返回時,UI 會縮放回上一個畫面。

iOS 上,當使用推播樣式轉場動畫時,Flutter 捆綁的 CupertinoNavigationBarCupertinoSliverNavigationBar 導覽列會自動將每個子組件的動畫移至下一個或上一個頁面的 CupertinoNavigationBarCupertinoSliverNavigationBar 上對應的子組件。

An animation of the page transition on Android
Android
An animation of the nav bar transitions during a page transition on iOS
iOS 導覽列

返回導覽

#

Android 上,預設會將 OS 返回按鈕傳送至 Flutter,並彈出 WidgetsApp 的 Navigator 的最上層路由。

iOS 上,可以使用邊緣滑動手勢來彈出最上層路由。

A page transition triggered by the Android back button
Android 返回按鈕
A page transition triggered by an iOS back swipe gesture
iOS 返回滑動手勢

捲動

#

滾動是平台外觀和風格的重要部分,而 Flutter 會自動調整滾動行為以符合目前的平台。

物理模擬

#

Android 和 iOS 都具有複雜的滾動物理模擬,難以用口頭描述。一般而言,iOS 的可滾動物件具有較大的重量和動態摩擦力,但 Android 具有較大的靜摩擦力。因此,iOS 會逐漸獲得高速,但不會突然停止,而且在慢速時更滑。

A soft fling where the iOS scrollable slid longer at lower speed than Android
軟滑動比較
A medium force fling where the Android scrollable reached speed faster and stopped more abruptly after reaching a longer distance
中等滑動比較
A strong fling where the Android scrollable reach speed faster and reached significantly more distance
強滑動比較

過度滾動行為

#

Android 上,滾動超出可滾動物件的邊緣會顯示 過度滾動發光指示器 (根據目前的 Material 主題顏色)。

iOS 上,滾動超出可滾動物件的邊緣會使用逐漸增加的阻力進行 過度滾動,然後彈回。

Android and iOS scrollables being flung past their edge and exhibiting platform specific overscroll behavior
動態過度滾動比較
Android and iOS scrollables being overscrolled from a resting position and exhibiting platform specific overscroll behavior
靜態過度滾動比較

慣性滑動

#

iOS 上,在相同方向重複滑動會堆疊動量,並在每次連續滑動時建立更高的速度。Android 上沒有等效的行為。

Repeated scroll flings building momentum on iOS
iOS 滾動慣性

返回頂部

#

iOS 上,點擊 OS 狀態列會將主要滾動控制器滾動至頂部位置。Android 上沒有等效的行為。

Tapping the status bar scrolls the primary scrollable back to the top
iOS 狀態列點擊以返回頂部

排版

#

當使用 Material 套件時,排版會自動預設為適合平台的字型系列。Android 使用 Roboto 字型。iOS 使用 San Francisco 字型。

當使用 Cupertino 套件時,預設主題 使用 San Francisco 字型。

San Francisco 字型授權限制其使用範圍僅限於在 iOS、macOS 或 tvOS 上執行的軟體。因此,如果平台偵錯覆寫為 iOS 或使用預設的 Cupertino 主題,則在 Android 上執行時會使用備用字型。

您可以選擇調整 Material Widget 的文字樣式,以符合 iOS 上的預設文字樣式。您可以在 UI 組件區段中看到特定於 Widget 的範例。

Roboto font on Android
Android 上的 Roboto
San Francisco font on iOS
iOS 上的 San Francisco

圖示

#

當使用 Material 套件時,某些圖示會根據平台自動顯示不同的圖形。例如,溢位按鈕的三個點在 iOS 上是水平的,在 Android 上是垂直的。返回按鈕在 iOS 上是一個簡單的 V 形符號,在 Android 上則有一個柄/軸。

Android appropriate icons
Android 上的圖示
iOS appropriate icons
iOS 上的圖示

Material 程式庫也透過 Icons.adaptive 提供一組平台自適應圖示。

觸覺回饋

#

Material 和 Cupertino 套件會在某些情況下自動觸發平台適當的觸覺回饋。

例如,透過文字欄位長按選取字詞會在 Android 上觸發「嗡嗡」振動,而在 iOS 上則不會。

在 iOS 上滾動瀏覽選擇器項目會觸發「輕微衝擊」敲擊,而在 Android 上則不會有回饋。

文字編輯

#

Material 和 Cupertino 文字輸入欄位都支援拼字檢查,並會調整以使用平台的正確拼字檢查設定,以及正確的拼字檢查功能表和醒目提示色彩。

Flutter 還會在編輯文字欄位的內容時進行以下調整,以符合目前的平台。

鍵盤手勢導覽

#

Android 上,可以在軟鍵盤的 空白鍵 上進行水平滑動,以在 Material 和 Cupertino 文字欄位中移動游標。

在具有 3D Touch 功能的 iOS 裝置上,可以在軟鍵盤上進行按壓拖曳手勢,以透過浮動游標在 2D 中移動游標。這適用於 Material 和 Cupertino 文字欄位。

Moving the cursor via the space key on Android
Android 空白鍵游標移動
Moving the cursor via 3D Touch drag on the keyboard on iOS
iOS 3D Touch 拖曳游標移動

文字選取工具列

#

使用 Android 上的 Material 時,當在文字欄位中進行文字選取時,會顯示 Android 樣式的選取工具列。

使用 iOS 上的 Material 或使用 Cupertino 時,當在文字欄位中進行文字選取時,會顯示 iOS 樣式的選取工具列。

Android appropriate text toolbar
Android 文字選取工具列
iOS appropriate text toolbar
iOS 文字選取工具列

單擊手勢

#

使用 Android 上的 Material 時,在文字欄位中單擊會將游標放在點擊的位置。

已收合的文字選取也會顯示可拖曳的控點,以便後續移動游標。

使用 iOS 上的 Material 或使用 Cupertino 時,在文字欄位中單擊會將游標放在點擊的字詞的最接近邊緣。

在 iOS 上,已收合的文字選取沒有可拖曳的控點。

Moving the cursor to the tapped position on Android
Android 點擊
Moving the cursor to the nearest edge of the tapped word on iOS
iOS 點擊

長按手勢

#

使用 Android 上的 Material 時,長按會選取長按下的字詞。選取工具列會在釋放時顯示。

使用 iOS 上的 Material 或使用 Cupertino 時,長按會將游標放在長按的位置。選取工具列會在釋放時顯示。

Selecting a word via long press on Android
Android 長按
Selecting a position via long press on iOS
iOS 長按

長按拖曳手勢

#

使用 Android 上的 Material 時,在按住長按的同時拖曳會展開選取的字詞。

使用 iOS 上的 Material 或使用 Cupertino 時,在按住長按的同時拖曳會移動游標。

Expanding word selection via long press drag on Android
Android 長按拖曳
Moving the cursor via long press drag on iOS
iOS 長按拖曳

雙擊手勢

#

在 Android 和 iOS 上,雙擊會選取接收雙擊的字詞,並顯示選取工具列。

Selecting a word via double tap on Android
Android 雙擊
Selecting a word via double tap on iOS
iOS 雙擊

UI 組件

#

本節包含有關如何調整 Material Widget 以在 iOS 上提供自然且引人入勝體驗的初步建議。歡迎您在 issue #8427 上提供意見反應。

具有 .adaptive() 建構子的 Widget

#

數個 Widget 支援 .adaptive() 建構子。下表列出這些 Widget。當應用程式在 iOS 裝置上執行時,自適應建構子會取代對應的 Cupertino 組件。

下表中的 Widget 主要用於輸入、選取和顯示系統資訊。由於這些控制項與作業系統緊密整合,因此使用者已訓練為能夠辨識並回應它們。因此,我們建議您遵循平台慣例。

Material WidgetCupertino Widget自適應建構子
Switch in Material 3
Switch
Switch in HIG
CupertinoSwitch
Switch.adaptive()
Slider in Material 3
Slider
Slider in HIG
CupertinoSlider
Slider.adaptive()
Circular progress indicator in Material 3
CircularProgressIndicator
Activity indicator in HIG
CupertinoActivityIndicator
CircularProgressIndicator.adaptive()
 Checkbox in Material 3
Checkbox
Checkbox in HIG
CupertinoCheckbox
Checkbox.adaptive()
Radio in Material 3
Radio
Radio in HIG
CupertinoRadio
Radio.adaptive()
AlertDialog in Material 3
AlertDialog
AlertDialog in HIG
CupertinoAlertDialog
AlertDialog.adaptive()

頂部應用程式列與導覽列

#

自 Android 12 起,頂部應用程式列的預設 UI 遵循 Material 3 中定義的設計指南。在 iOS 上,一個名為「導覽列」的等效組件定義在 Apple 的人機介面指南 (HIG) 中。

 Top App Bar in Material 3
Material 3 中的頂部應用程式列
Navigation Bar in Human Interface Guidelines
人機介面指南中的導覽列

Flutter 應用程式中應用程式列的某些屬性應進行調整,例如系統圖示和頁面轉換。當使用 Material 的 AppBarSliverAppBar 小工具時,這些已經會自動調整。您也可以進一步自訂這些小工具的屬性,使其更符合 iOS 平台樣式,如下所示。

dart
// Map the text theme to iOS styles
TextTheme cupertinoTextTheme = TextTheme(
    headlineMedium: CupertinoThemeData()
        .textTheme
        .navLargeTitleTextStyle
         // fixes a small bug with spacing
        .copyWith(letterSpacing: -1.5),
    titleLarge: CupertinoThemeData().textTheme.navTitleTextStyle)
...

// Use iOS text theme on iOS devices
ThemeData(
      textTheme: Platform.isIOS ? cupertinoTextTheme : null,
      ...
)
...

// Modify AppBar properties
AppBar(
        surfaceTintColor: Platform.isIOS ? Colors.transparent : null,
        shadowColor: Platform.isIOS ? CupertinoColors.darkBackgroundGray : null,
        scrolledUnderElevation: Platform.isIOS ? .1 : null,
        toolbarHeight: Platform.isIOS ? 44 : null,
        ...
      ),

但是,由於應用程式列與頁面中的其他內容一同顯示,因此建議僅在與應用程式的其餘部分保持一致的情況下調整樣式。您可以在 GitHub 上關於應用程式列調整的討論中查看其他程式碼範例和進一步說明。

底部導覽列

#

自 Android 12 起,底部導覽列的預設 UI 遵循 Material 3 中定義的設計指南。在 iOS 上,一個名為「分頁標籤列」的等效組件定義在 Apple 的人機介面指南 (HIG) 中。

Bottom Navigation Bar in Material 3
Material 3 中的底部導覽列
Tab Bar in Human Interface Guidelines
人機介面指南中的分頁標籤列

由於分頁標籤列在您的應用程式中是持久存在的,它們應該與您自己的品牌形象一致。但是,如果您選擇在 Android 上使用 Material 的預設樣式,您可以考慮調整為 iOS 的預設分頁標籤列。

要實作特定平台的底部導覽列,您可以在 Android 上使用 Flutter 的 NavigationBar 小工具,在 iOS 上使用 CupertinoTabBar 小工具。以下程式碼片段您可以調整以顯示特定平台的導覽列。

dart
final Map<String, Icon> _navigationItems = {
    'Menu': Platform.isIOS ? Icon(CupertinoIcons.house_fill) : Icon(Icons.home),
    'Order': Icon(Icons.adaptive.share),
  };

...

Scaffold(
  body: _currentWidget,
  bottomNavigationBar: Platform.isIOS
          ? CupertinoTabBar(
              currentIndex: _currentIndex,
              onTap: (index) {
                setState(() => _currentIndex = index);
                _loadScreen();
              },
              items: _navigationItems.entries
                  .map<BottomNavigationBarItem>(
                      (entry) => BottomNavigationBarItem(
                            icon: entry.value,
                            label: entry.key,
                          ))
                  .toList(),
            )
          : NavigationBar(
              selectedIndex: _currentIndex,
              onDestinationSelected: (index) {
                setState(() => _currentIndex = index);
                _loadScreen();
              },
              destinations: _navigationItems.entries
                  .map<Widget>((entry) => NavigationDestination(
                        icon: entry.value,
                        label: entry.key,
                      ))
                  .toList(),
            ));

文字輸入框

#

自 Android 12 起,文字欄位遵循 Material 3 (M3) 設計指南。在 iOS 上,Apple 的 人機介面指南 (HIG) 定義了一個等效的組件。

Text Field in Material 3
Material 3 中的文字欄位
Text Field in Human Interface Guidelines
HIG 中的文字欄位

由於文字欄位需要使用者輸入,
其設計應遵循平台慣例。

要在 Flutter 中實作特定平台的 TextField,您可以調整 Material 的 TextField 的樣式。

dart
Widget _createAdaptiveTextField() {
  final _border = OutlineInputBorder(
    borderSide: BorderSide(color: CupertinoColors.lightBackgroundGray),
  );

  final iOSDecoration = InputDecoration(
    border: _border,
    enabledBorder: _border,
    focusedBorder: _border,
    filled: true,
    fillColor: CupertinoColors.white,
    hoverColor: CupertinoColors.white,
    contentPadding: EdgeInsets.fromLTRB(10, 0, 0, 0),
  );

  return Platform.isIOS
      ? SizedBox(
          height: 36.0,
          child: TextField(
            decoration: iOSDecoration,
          ),
        )
      : TextField();
}

要了解更多關於調整文字欄位的資訊,請查看 GitHub 上關於文字欄位的討論。您可以在討論中留下回饋或提問。