跳到主要內容

Android 預測性返回

摘要

#

為了支援 Android 14 的預測性返回功能,一組預先 (ahead-of-time) 的 API 已取代即時 (just-in-time) 的導航 API,例如 WillPopScopeNavigator.willPop

背景

#

Android 14 引入了預測性返回功能,它允許使用者在有效的返回手勢期間預覽目前路由後面的內容,並決定是否繼續返回或取消手勢。這與 Flutter 的導航 API 不相容,因為 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 取代,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