自動平台調整
適應性設計理念
#一般而言,平台適應性存在兩種情況
- 屬於作業系統環境行為 (例如文字編輯和滾動),如果發生不同行為就會「錯誤」的情況。
- 通常在使用 OEM 的 SDK 的應用程式中實現的事物 (例如在 iOS 上使用並排標籤或在 Android 上顯示
android.app.AlertDialog
)。
本文主要涵蓋 Flutter 在 Android 和 iOS 上針對第一種情況提供的自動適應。
對於第二種情況,Flutter 捆綁了產生平台慣例的適當效果的方法,但在需要應用程式設計選擇時不會自動適應。如需討論,請參閱 issue #8410 和 Material/Cupertino 自適應 Widget 問題定義。
如需在 Android 和 iOS 上使用不同資訊架構結構,但共用相同內容程式碼的應用程式範例,請參閱 platform_design 程式碼範例。
頁面導覽
#Flutter 提供 Android 和 iOS 上可見的導覽模式,並且會自動將導覽動畫調整為目前的平台。
導覽轉場動畫
#在 Android 上,預設的 Navigator.push()
轉場動畫是以 startActivity()
為模型,通常具有一個由下往上的動畫變體。
在 iOS 上
- 預設的
Navigator.push()
API 會產生 iOS 顯示/推播樣式轉場動畫,該動畫會根據地區設定的 RTL 設定,從結束到開始進行動畫。新路由後面的頁面也會以與 iOS 中相同的方向進行視差滑動。 - 當推播頁面路由且
PageRoute.fullscreenDialog
為 true 時,會存在一個單獨的由下往上轉場樣式。這表示 iOS 的顯示/強制回應樣式轉場動畫,通常用於全螢幕強制回應頁面。
平台特定轉場細節
#在 Android 上,Flutter 使用 ZoomPageTransitionsBuilder
動畫。當使用者點擊某個項目時,UI 會縮放至以該項目為特色的畫面。當使用者點擊返回時,UI 會縮放回上一個畫面。
在 iOS 上,當使用推播樣式轉場動畫時,Flutter 捆綁的 CupertinoNavigationBar
和 CupertinoSliverNavigationBar
導覽列會自動將每個子組件的動畫移至下一個或上一個頁面的 CupertinoNavigationBar
或 CupertinoSliverNavigationBar
上對應的子組件。
返回導覽
#在 Android 上,預設會將 OS 返回按鈕傳送至 Flutter,並彈出 WidgetsApp
的 Navigator 的最上層路由。
在 iOS 上,可以使用邊緣滑動手勢來彈出最上層路由。
捲動
#滾動是平台外觀和風格的重要部分,而 Flutter 會自動調整滾動行為以符合目前的平台。
物理模擬
#Android 和 iOS 都具有複雜的滾動物理模擬,難以用口頭描述。一般而言,iOS 的可滾動物件具有較大的重量和動態摩擦力,但 Android 具有較大的靜摩擦力。因此,iOS 會逐漸獲得高速,但不會突然停止,而且在慢速時更滑。
過度滾動行為
#在 Android 上,滾動超出可滾動物件的邊緣會顯示 過度滾動發光指示器 (根據目前的 Material 主題顏色)。
在 iOS 上,滾動超出可滾動物件的邊緣會使用逐漸增加的阻力進行 過度滾動,然後彈回。
慣性滑動
#在 iOS 上,在相同方向重複滑動會堆疊動量,並在每次連續滑動時建立更高的速度。Android 上沒有等效的行為。
返回頂部
#在 iOS 上,點擊 OS 狀態列會將主要滾動控制器滾動至頂部位置。Android 上沒有等效的行為。
排版
#當使用 Material 套件時,排版會自動預設為適合平台的字型系列。Android 使用 Roboto 字型。iOS 使用 San Francisco 字型。
當使用 Cupertino 套件時,預設主題 使用 San Francisco 字型。
San Francisco 字型授權限制其使用範圍僅限於在 iOS、macOS 或 tvOS 上執行的軟體。因此,如果平台偵錯覆寫為 iOS 或使用預設的 Cupertino 主題,則在 Android 上執行時會使用備用字型。
您可以選擇調整 Material Widget 的文字樣式,以符合 iOS 上的預設文字樣式。您可以在 UI 組件區段中看到特定於 Widget 的範例。
圖示
#當使用 Material 套件時,某些圖示會根據平台自動顯示不同的圖形。例如,溢位按鈕的三個點在 iOS 上是水平的,在 Android 上是垂直的。返回按鈕在 iOS 上是一個簡單的 V 形符號,在 Android 上則有一個柄/軸。
Material 程式庫也透過 Icons.adaptive
提供一組平台自適應圖示。
觸覺回饋
#Material 和 Cupertino 套件會在某些情況下自動觸發平台適當的觸覺回饋。
例如,透過文字欄位長按選取字詞會在 Android 上觸發「嗡嗡」振動,而在 iOS 上則不會。
在 iOS 上滾動瀏覽選擇器項目會觸發「輕微衝擊」敲擊,而在 Android 上則不會有回饋。
文字編輯
#Material 和 Cupertino 文字輸入欄位都支援拼字檢查,並會調整以使用平台的正確拼字檢查設定,以及正確的拼字檢查功能表和醒目提示色彩。
Flutter 還會在編輯文字欄位的內容時進行以下調整,以符合目前的平台。
鍵盤手勢導覽
#在 Android 上,可以在軟鍵盤的 空白鍵 上進行水平滑動,以在 Material 和 Cupertino 文字欄位中移動游標。
在具有 3D Touch 功能的 iOS 裝置上,可以在軟鍵盤上進行按壓拖曳手勢,以透過浮動游標在 2D 中移動游標。這適用於 Material 和 Cupertino 文字欄位。
文字選取工具列
#使用 Android 上的 Material 時,當在文字欄位中進行文字選取時,會顯示 Android 樣式的選取工具列。
使用 iOS 上的 Material 或使用 Cupertino 時,當在文字欄位中進行文字選取時,會顯示 iOS 樣式的選取工具列。
單擊手勢
#使用 Android 上的 Material 時,在文字欄位中單擊會將游標放在點擊的位置。
已收合的文字選取也會顯示可拖曳的控點,以便後續移動游標。
使用 iOS 上的 Material 或使用 Cupertino 時,在文字欄位中單擊會將游標放在點擊的字詞的最接近邊緣。
在 iOS 上,已收合的文字選取沒有可拖曳的控點。
長按手勢
#使用 Android 上的 Material 時,長按會選取長按下的字詞。選取工具列會在釋放時顯示。
使用 iOS 上的 Material 或使用 Cupertino 時,長按會將游標放在長按的位置。選取工具列會在釋放時顯示。
長按拖曳手勢
#使用 Android 上的 Material 時,在按住長按的同時拖曳會展開選取的字詞。
使用 iOS 上的 Material 或使用 Cupertino 時,在按住長按的同時拖曳會移動游標。
雙擊手勢
#在 Android 和 iOS 上,雙擊會選取接收雙擊的字詞,並顯示選取工具列。
UI 組件
#本節包含有關如何調整 Material Widget 以在 iOS 上提供自然且引人入勝體驗的初步建議。歡迎您在 issue #8427 上提供意見反應。
具有 .adaptive() 建構子的 Widget
#數個 Widget 支援 .adaptive()
建構子。下表列出這些 Widget。當應用程式在 iOS 裝置上執行時,自適應建構子會取代對應的 Cupertino 組件。
下表中的 Widget 主要用於輸入、選取和顯示系統資訊。由於這些控制項與作業系統緊密整合,因此使用者已訓練為能夠辨識並回應它們。因此,我們建議您遵循平台慣例。
Material Widget | Cupertino Widget | 自適應建構子 |
---|---|---|
Switch | CupertinoSwitch | Switch.adaptive() |
Slider | CupertinoSlider | Slider.adaptive() |
CircularProgressIndicator | CupertinoActivityIndicator | CircularProgressIndicator.adaptive() |
Checkbox | CupertinoCheckbox | Checkbox.adaptive() |
Radio | CupertinoRadio | Radio.adaptive() |
AlertDialog | CupertinoAlertDialog | AlertDialog.adaptive() |
頂部應用程式列與導覽列
#自 Android 12 起,頂部應用程式列的預設 UI 遵循 Material 3 中定義的設計指南。在 iOS 上,一個名為「導覽列」的等效組件定義在 Apple 的人機介面指南 (HIG) 中。
Flutter 應用程式中應用程式列的某些屬性應進行調整,例如系統圖示和頁面轉換。當使用 Material 的 AppBar
和 SliverAppBar
小工具時,這些已經會自動調整。您也可以進一步自訂這些小工具的屬性,使其更符合 iOS 平台樣式,如下所示。
// 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) 中。
由於分頁標籤列在您的應用程式中是持久存在的,它們應該與您自己的品牌形象一致。但是,如果您選擇在 Android 上使用 Material 的預設樣式,您可以考慮調整為 iOS 的預設分頁標籤列。
要實作特定平台的底部導覽列,您可以在 Android 上使用 Flutter 的 NavigationBar
小工具,在 iOS 上使用 CupertinoTabBar
小工具。以下程式碼片段您可以調整以顯示特定平台的導覽列。
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) 定義了一個等效的組件。
由於文字欄位需要使用者輸入,
其設計應遵循平台慣例。
要在 Flutter 中實作特定平台的 TextField
,您可以調整 Material 的 TextField
的樣式。
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 上關於文字欄位的討論。您可以在討論中留下回饋或提問。
除非另有說明,本網站上的文件反映了 Flutter 的最新穩定版本。頁面最後更新於 2024-07-30。 查看原始碼 或 回報問題。