跳至主要内容

Android 预测返回

摘要

#

为了支持 Android 14 的预测返回功能,一组提前的 API 已经取代了即时的导航 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: ...
),

从 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 方法。此方法已被公开其 canPoponPopInvoked 方法的 PopScope 取代。迁移过程与从 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 来跟踪其小部件子树中 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