不同平台操作體驗的差異和適配

適配哲學

平台適配通常有兩種情形:

  1. 作業系統所特有的操作體驗(例如文字編輯和滾動)。如果操作體驗與作業系統不一致,則通常會被認為是“錯誤的”。

  2. 使用 OEM 提供的 SDK 實現的功能體驗(例如 iOS 常使用的選項卡, Android 使用 android.app.AlertDialog 顯示一個提示視窗)。

本文囊括了 Flutter 為解決情形 1 而提供的覆蓋 Android 和 iOS 的自動適配。

對於情形 2,Flutter 提供了一些工具可以產生符合平台習慣的體驗,但是不會根據平台自動適配,需要根據 App 設計來手工選擇。更多有關的討論,請存取 issue #8410 和這個文件 定義 Material/Cupertino widget 適配問題

如果一個應用需要在 Android 和 iOS 不同架構上使用相同的程式碼,請參閱 platform_design 這份程式碼範例

Flutter 分別為 Android 和 iOS 提供了各自平台的導航模式,並根據當前平台自動適配導航轉場動畫。

Android 平台,預設提供的 Navigator.push() 轉場動畫模仿了 startActivity() 的動畫,即一種自下而上的動畫效果。

iOS 平台:

  • iOS 的 Navigator.push() API 提供了 iOS 上的 Show 轉場動畫(也被稱為 Push 轉場動畫),即根據語言的方向設定,執行一種從後到前的滾動動畫效果。在顯示新頁面的時候,原來的頁面也會沿著相同的方向進行視差滾動。

  • 當顯示一個頁面,且 PageRoute.fullscreenDialog 是 true 的時候, iOS 提供了另外一種自下而上的動畫效果。這個動畫通常被用在展示全屏模態頁,也被稱為 iOS 上的 Present 轉場動畫或 Modal 轉場動畫。

An animation of the bottom-up page transition on Android
Android 轉場動畫
An animation of the end-start style push page transition on iOS
iOS Push 轉場動畫
An animation of the bottom-up style present page transition on iOS
iOS Present 轉場動畫

不同平台的轉場動畫細節

Android 平臺上,根據你的作業系統版本差異,有兩種不同的轉場動畫:

  • API 28 版本之前的系統,提供了一種自下而上 滾動並淡出 的動畫效果。

  • API 28 和以後的系統,則提供了另外一種自下而上 滾動並反向翻轉 的動畫效果。

當在 iOS 平臺上使用 Push 轉場特效的時候, Flutter 內建的 CupertinoNavigationBarCupertinoSliverNavigationBar 會自動的給當前頁下一頁的子元件使用正確的動畫效果(CupertinoNavigationBar 或者 CupertinoSliverNavigationBar)。

An animation of the page transition on Android pre-Android P
Android 9 以前
An animation of the page transition on Android on Android P
Android 9 以後
An animation of the nav bar transitions during a page transition on iOS
iOS 導航欄

返回導航

Android 平台,通常作業系統的返回按鈕觸發的事件會發給 Flutter,並彈出 WidgetsApp 路由的最頂端。

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 平台,點選作業系統的狀態列,主要的捲軸控制器會滾動到頂部。 Android 沒有對應的行為。

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

排版

當使用 Material package 的時候,排版會根據平台自動使用對應的字型。 Android 平台會使用 Roboto 字型,而 iOS 則會使用系統自帶的 San Francisco 字型。

當使用 Cupertino 套件的時候,預設主題 會一直使用 San Francisco 字型。

San Francisco 字型的授權限制了它只能被用在運行於 iOS、macOS 和 tvOS 平臺上的軟體。因此當執行在 Android 平台的時候,即使強制覆蓋系統平台為 iOS 或者使用 Cupertino 預設主題,都會使用對應的替代字型。

Roboto font on Android
Android 平台 Robot 字型
San Francisco font on iOS
iOS 平台 San Francisco 字型

圖示

當使用 Material 套件的時候,根據平台不同,圖示的具體樣式會有差別。舉例來說,更多按鈕的圖示,Android 上是豎直的三個點而 iOS 是橫著的三個點;退回按鈕,iOS 是一個簡單的 V 型標記,而 Android 平台,V 型標記有個短橫線。

Android appropriate icons
Android 平台圖示
iOS appropriate icons
iOS 平台圖示

The material library also provides a set of platform-adaptive icons through Icons.adaptive.

觸控反饋

Material 和 Cupertino 包在特定場景下都會自動觸發符合平台特點的觸控反饋。

例如,在文字輸入框控制項裡面長按選中單詞會在 Android 裝置上會觸發震動,而 iOS 不會。

在 iOS 滾動選擇器專案列表,會觸發一個很輕的敲擊音效,而 Android 則不會。

文字編輯

Flutter 會根據當前平台來適配正確的文字編輯體驗。

鍵盤手勢導航

Android 平台,在虛擬鍵盤空格鍵上可以透過左右輕掃來移動游標, Material 和 Cupertino 的文字輸入框控制項都支援該特性。

iOS 裝置提供了 3D Touch 相容,透過在虛擬鍵盤上使用長按並拖拽手勢可以任意方向移動游標。 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 雙擊