概述

#

為了支援 Android 14 的預測回退功能,一系列預先配置的 API 已取代了即時導航 API,例如 WillPopScopeNavigator.willPop

背景

#

Android 14 引入了 預測回退功能,該功能允許使用者在有效的回退手勢期間預覽當前路由之前的介面,並決定是繼續回退還是取消手勢。這與 Flutter 允許開發者在接收到回退手勢後取消回退手勢的導航 API 不相容。

在預測回退中,當用戶啟動手勢時,回退動畫會立即開始,並在提交之前。Flutter 應用沒有機會在此時間決定是否允許回退。這必須提前知道。

因此,所有允許 Flutter 應用開發者在接收到回退手勢時取消回退導航的 API 都已棄用。它們已被等效的 API 取代,這些 API 始終維護一個布林狀態,決定是否可能回退導航。當可能時,預測回退動畫會照常發生。否則,導航會被停止。在這兩種情況下,應用開發者都會收到有關是否嘗試回退以及是否成功的回撥。

PopScope

#

PopScope 類直接取代 WillPopScope 以啟用預測回退。它不是在發生回退時決定是否可能回退,而是透過 canPop 布林值提前設定。您仍然可以使用 onPopInvoked 來監聽回退。

dart
PopScope(
  canPop: _myPopDisableEnableLogic(),
  onPopInvoked: (bool didPop) {
    // Handle the pop. If `didPop` is false, it was blocked.
  },
)

Form.canPop 和 Form.onPopInvoked

#

這兩個新引數基於 PopScope,並取代了已棄用的 Form.onWillPop 引數。它們與 PopScope 的用法與上面相同。

dart
Form(
  canPop: _myPopDisableEnableLogic(),
  onPopInvoked: (bool didPop) {
    // Handle the pop. If `didPop` is false, it was blocked.
  },
)

Route.popDisposition

#

此 getter 同步返回路由的 RoutePopDisposition,該屬性描述了回退行為。

dart
if (myRoute.popDisposition == RoutePopDisposition.doNotPop) {
  // Back gestures are disabled.
}

ModalRoute.registerPopEntry 和 ModalRoute.unregisterPopEntry

#

使用這些方法註冊 PopScope 小部件,以便在路由決定是否可以回退時進行評估。當實現自定義 PopScope 小部件時,可能會用到此功能。

dart
@override
void didChangeDependencies() {
  super.didChangeDependencies();
  final ModalRoute<dynamic>? nextRoute = ModalRoute.of(context);
  if (nextRoute != _route) {
    _route?.unregisterPopEntry(this);
    _route = nextRoute;
    _route?.registerPopEntry(this);
  }
}

遷移指南

#

WillPopScope 遷移到 PopScope

#

WillPopScope 小部件的直接替代品是 PopScope 小部件。在許多情況下,onWillPop 中在回退手勢發生時執行的邏輯可以在構建時完成並設定為 canPop

遷移前的程式碼

dart
WillPopScope(
  onWillPop: () async {
    return _myCondition;
  },
  child: ...
),

遷移後的程式碼

dart
PopScope(
  canPop: _myCondition,
  child: ...
),

對於需要通知回退嘗試的情況,onPopInvoked 方法可以以類似於 onWillPop 的方式使用。請注意,雖然 onWillPop 在回退被處理之前被呼叫,並且有能力取消它,但 onPopInvoked 在回退處理完成後被呼叫。

遷移前的程式碼

dart
WillPopScope(
  onWillPop: () async {
    _myHandleOnPopMethod();
    return true;
  },
  child: ...
),

遷移後的程式碼

dart
PopScope(
  canPop: true,
  onPopInvoked: (bool didPop) {
    _myHandleOnPopMethod();
  },
  child: ...
),

為巢狀 Navigator 從 WillPopScope 遷移到 NavigatorPopHandler

#

WillPopScope 的一個非常常見的用例是在使用巢狀 Navigator 小部件時正確處理回退手勢。使用 PopScope 也可以做到這一點,但現在有一個包裝器小部件使此操作更加容易:NavigatorPopHandler

遷移前的程式碼

dart
WillPopScope(
  onWillPop: () async => !(await _nestedNavigatorKey.currentState!.maybePop()),
  child: Navigator(
    key: _nestedNavigatorKey,

  ),
)

遷移後的程式碼

dart
NavigatorPopHandler(
  onPop: () => _nestedNavigatorKey.currentState!.pop(),
  child: Navigator(
    key: _nestedNavigatorKey,

  ),
)

從 Form.onWillPop 遷移到 Form.canPop 和 Form.onPopInvoked

#

以前,Form 在底層使用了 WillPopScope 例項並暴露了其 onWillPop 方法。這已被 PopScope 取代,後者暴露了其 canPoponPopInvoked 方法。遷移與上面詳細介紹的從 WillPopScope 遷移到 PopScope 的方法相同。

從 Route.willPop 遷移到 Route.popDisposition

#

RoutewillPop 方法返回一個 Future<RoutePopDisposition>,以便適應回退可能被取消的事實。現在由於這不再是必需的,此邏輯已簡化為同步 getter。

遷移前的程式碼

dart
if (await myRoute.willPop() == RoutePopDisposition.doNotPop) {
  ...
}

遷移後的程式碼

dart
if (myRoute.popDisposition == RoutePopDisposition.doNotPop) {
  ...
}

從 ModalRoute.add/removeScopedWillPopCallback 遷移到 ModalRoute.(un)registerPopEntry

#

在內部,ModalRoute 透過使用 addScopedWillPopCallbackremoveScopedWillPopCallback 註冊來跟蹤其小部件樹中 WillPopScope 的存在。由於 PopScope 取代了 WillPopScope,這些方法已分別被 registerPopEntryunregisterPopEntry 取代。

PopEntryPopScope 實現,以便僅向 ModalRoute 暴露最少必要資訊。任何編寫自己的 PopScope 的人應該實現 PopEntry 並將其小部件註冊和登出到其封閉的 ModalRoute

遷移前的程式碼

dart
@override
void didChangeDependencies() {
  super.didChangeDependencies();
  if (widget.onWillPop != null) {
    _route?.removeScopedWillPopCallback(widget.onWillPop!);
  }
  _route = ModalRoute.of(context);
  if (widget.onWillPop != null) {
    _route?.addScopedWillPopCallback(widget.onWillPop!);
  }
}

遷移後的程式碼

dart
@override
void didChangeDependencies() {
  super.didChangeDependencies();
  _route?.unregisterPopEntry(this);
  _route = ModalRoute.of(context);
  _route?.registerPopEntry(this);
}

從 ModalRoute.hasScopedWillPopCallback 遷移到 ModalRoute.popDisposition

#

此方法以前用於非常類似於預測回退但發生在 Cupertino 庫中的用例,其中某些回退轉換允許取消導航。當存在 WillPopScope 小部件可能取消回退時,路由轉換將被停用。

現在 API 要求提前決定,這不再需要基於 PopScope 小部件的存在來推測。ModalRoute 是否被 PopScope 小部件阻止回退的確切邏輯已內建到 ModalRoute.popDisposition 中。

遷移前的程式碼

dart
if (_route.hasScopedWillPopCallback) {
  // Disable predictive route transitions.
}

遷移後的程式碼

dart
if (_route.popDisposition == RoutePopDisposition.doNotPop) {
  // Disable predictive route transitions.
}

遷移確認回退對話方塊

#

WillPopScope 有時用於在接收到回退手勢時顯示確認對話方塊。使用 PopScope 仍然可以按類似模式完成此操作。

遷移前的程式碼

dart
WillPopScope(
  onWillPop: () async {
    final bool? shouldPop = await _showBackDialog();
    return shouldPop ?? false;
  },
  child: child,
)

遷移後的程式碼

dart
return PopScope(
  canPop: false,
  onPopInvoked: (bool didPop) async {
    if (didPop) {
      return;
    }
    final NavigatorState navigator = Navigator.of(context);
    final bool? shouldPop = await _showBackDialog();
    if (shouldPop ?? false) {
      navigator.pop();
    }
  },
  child: child,
)

支援預測回退

#
  1. 執行 Android 14 (API 級別 34) 或更高版本。
  2. 在裝置上的“開發者選項”中啟用預測回退功能標誌。在未來的 Android 版本中這將不再需要。
  3. android/app/src/main/AndroidManifest.xml 中設定 android:enableOnBackInvokedCallback="true"。如果需要,請參閱 Android 官方指南,瞭解如何遷移 Android 應用以支援預測回退。
  4. 確保您使用的是 Flutter 3.14.0-7.0.pre 或更高版本。
  5. 確保您的 Flutter 應用不使用 WillPopScope 小部件。使用它會停用預測回退。如有需要,請使用 PopScope 代替。
  6. 執行應用並執行回退手勢(從螢幕左側滑動)。

時間線

#

首次釋出版本:3.14.0-7.0.pre
穩定版本:3.16

參考資料

#

API 文件

相關問題

相關 PR