Android 預測回退
概述
#為了支援 Android 14 的預測回退功能,一系列預先配置的 API 已取代了即時導航 API,例如 WillPopScope 和 Navigator.willPop。
背景
#Android 14 引入了 預測回退功能,該功能允許使用者在有效的回退手勢期間預覽當前路由之前的介面,並決定是繼續回退還是取消手勢。這與 Flutter 允許開發者在接收到回退手勢後取消回退手勢的導航 API 不相容。
在預測回退中,當用戶啟動手勢時,回退動畫會立即開始,並在提交之前。Flutter 應用沒有機會在此時間決定是否允許回退。這必須提前知道。
因此,所有允許 Flutter 應用開發者在接收到回退手勢時取消回退導航的 API 都已棄用。它們已被等效的 API 取代,這些 API 始終維護一個布林狀態,決定是否可能回退導航。當可能時,預測回退動畫會照常發生。否則,導航會被停止。在這兩種情況下,應用開發者都會收到有關是否嘗試回退以及是否成功的回撥。
PopScope
#PopScope 類直接取代 WillPopScope 以啟用預測回退。它不是在發生回退時決定是否可能回退,而是透過 canPop 布林值提前設定。您仍然可以使用 onPopInvoked 來監聽回退。
PopScope(
canPop: _myPopDisableEnableLogic(),
onPopInvoked: (bool didPop) {
// Handle the pop. If `didPop` is false, it was blocked.
},
)Form.canPop 和 Form.onPopInvoked
#這兩個新引數基於 PopScope,並取代了已棄用的 Form.onWillPop 引數。它們與 PopScope 的用法與上面相同。
Form(
canPop: _myPopDisableEnableLogic(),
onPopInvoked: (bool didPop) {
// Handle the pop. If `didPop` is false, it was blocked.
},
)Route.popDisposition
#此 getter 同步返回路由的 RoutePopDisposition,該屬性描述了回退行為。
if (myRoute.popDisposition == RoutePopDisposition.doNotPop) {
// Back gestures are disabled.
}ModalRoute.registerPopEntry 和 ModalRoute.unregisterPopEntry
#使用這些方法註冊 PopScope 小部件,以便在路由決定是否可以回退時進行評估。當實現自定義 PopScope 小部件時,可能會用到此功能。
@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。
遷移前的程式碼
WillPopScope(
onWillPop: () async {
return _myCondition;
},
child: ...
),遷移後的程式碼
PopScope(
canPop: _myCondition,
child: ...
),對於需要通知回退嘗試的情況,onPopInvoked 方法可以以類似於 onWillPop 的方式使用。請注意,雖然 onWillPop 在回退被處理之前被呼叫,並且有能力取消它,但 onPopInvoked 在回退處理完成後被呼叫。
遷移前的程式碼
WillPopScope(
onWillPop: () async {
_myHandleOnPopMethod();
return true;
},
child: ...
),遷移後的程式碼
PopScope(
canPop: true,
onPopInvoked: (bool didPop) {
_myHandleOnPopMethod();
},
child: ...
),為巢狀 Navigator 從 WillPopScope 遷移到 NavigatorPopHandler
#WillPopScope 的一個非常常見的用例是在使用巢狀 Navigator 小部件時正確處理回退手勢。使用 PopScope 也可以做到這一點,但現在有一個包裝器小部件使此操作更加容易:NavigatorPopHandler。
遷移前的程式碼
WillPopScope(
onWillPop: () async => !(await _nestedNavigatorKey.currentState!.maybePop()),
child: Navigator(
key: _nestedNavigatorKey,
…
),
)遷移後的程式碼
NavigatorPopHandler(
onPop: () => _nestedNavigatorKey.currentState!.pop(),
child: Navigator(
key: _nestedNavigatorKey,
…
),
)從 Form.onWillPop 遷移到 Form.canPop 和 Form.onPopInvoked
#以前,Form 在底層使用了 WillPopScope 例項並暴露了其 onWillPop 方法。這已被 PopScope 取代,後者暴露了其 canPop 和 onPopInvoked 方法。遷移與上面詳細介紹的從 WillPopScope 遷移到 PopScope 的方法相同。
從 Route.willPop 遷移到 Route.popDisposition
#Route 的 willPop 方法返回一個 Future<RoutePopDisposition>,以便適應回退可能被取消的事實。現在由於這不再是必需的,此邏輯已簡化為同步 getter。
遷移前的程式碼
if (await myRoute.willPop() == RoutePopDisposition.doNotPop) {
...
}遷移後的程式碼
if (myRoute.popDisposition == RoutePopDisposition.doNotPop) {
...
}從 ModalRoute.add/removeScopedWillPopCallback 遷移到 ModalRoute.(un)registerPopEntry
#在內部,ModalRoute 透過使用 addScopedWillPopCallback 和 removeScopedWillPopCallback 註冊來跟蹤其小部件樹中 WillPopScope 的存在。由於 PopScope 取代了 WillPopScope,這些方法已分別被 registerPopEntry 和 unregisterPopEntry 取代。
PopEntry 由 PopScope 實現,以便僅向 ModalRoute 暴露最少必要資訊。任何編寫自己的 PopScope 的人應該實現 PopEntry 並將其小部件註冊和登出到其封閉的 ModalRoute。
遷移前的程式碼
@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!);
}
}遷移後的程式碼
@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 中。
遷移前的程式碼
if (_route.hasScopedWillPopCallback) {
// Disable predictive route transitions.
}遷移後的程式碼
if (_route.popDisposition == RoutePopDisposition.doNotPop) {
// Disable predictive route transitions.
}遷移確認回退對話方塊
#WillPopScope 有時用於在接收到回退手勢時顯示確認對話方塊。使用 PopScope 仍然可以按類似模式完成此操作。
遷移前的程式碼
WillPopScope(
onWillPop: () async {
final bool? shouldPop = await _showBackDialog();
return shouldPop ?? false;
},
child: child,
)遷移後的程式碼
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,
)支援預測回退
#- 執行 Android 14 (API 級別 34) 或更高版本。
- 在裝置上的“開發者選項”中啟用預測回退功能標誌。在未來的 Android 版本中這將不再需要。
- 在
android/app/src/main/AndroidManifest.xml中設定android:enableOnBackInvokedCallback="true"。如果需要,請參閱 Android 官方指南,瞭解如何遷移 Android 應用以支援預測回退。 - 確保您使用的是 Flutter
3.14.0-7.0.pre或更高版本。 - 確保您的 Flutter 應用不使用
WillPopScope小部件。使用它會停用預測回退。如有需要,請使用PopScope代替。 - 執行應用並執行回退手勢(從螢幕左側滑動)。
時間線
#首次釋出版本:3.14.0-7.0.pre
穩定版本:3.16
參考資料
#API 文件
PopScopeNavigatorPopHandlerPopEntryForm.canPopForm.onPopInvokedRoute.popDispositionModalRoute.registerPopEntryModalRoute.unregisterPopEntry
相關問題
相關 PR