Android 预测返回
- 摘要
 - 背景
 - 迁移指南
- 从 WillPopScope 迁移到 PopScope
 - 从 WillPopScope 迁移到 NavigatorPopHandler 以处理嵌套的 Navigators
 - 从 Form.onWillPop 迁移到 Form.canPop 和 Form.onPopInvoked
 - 从 Route.willPop 迁移到 Route.popDisposition
 - 从 ModalRoute.add/removeScopedWillPopCallback 迁移到 ModalRoute.(un)registerPopEntry
 - 从 ModalRoute.hasScopedWillPopCallback 迁移到 ModalRoute.popDisposition
 - 迁移返回确认对话框
 - 支持预测返回
 
 - 时间线
 - 参考
 
摘要
#为了支持 Android 14 的预测返回功能,一组提前执行的 API 已经替换了即时导航 API,例如 WillPopScope 和 Navigator.willPop。
背景
#Android 14 引入了 预测返回功能, 允许用户在有效的返回手势期间查看当前路由后面的内容,并决定是继续返回还是取消手势。这与 Flutter 的导航 API 不兼容,因为 Flutter 的导航 API 允许开发者在接收到返回手势后取消返回手势。
使用预测返回功能时,当用户启动手势并且在手势提交之前,返回动画会立即开始。Flutter 应用没有机会在这个时间点决定是否允许返回。它必须提前知道。
因此,所有允许 Flutter 应用开发者在接收到返回手势时取消返回导航的 API 现在都已弃用。它们已被等效的 API 替换,这些 API 始终维护一个布尔状态,该状态决定是否可以进行返回导航。如果可以,则预测返回动画照常进行。否则,导航将停止。在这两种情况下,应用开发者都会收到关于返回尝试及其是否成功的通知。
PopScope
#PopScope 类直接替换 WillPopScope 以启用预测返回。它不是在弹出发生时决定是否可以弹出,而是在提前使用布尔值 canPop 设置。您仍然可以使用 onPopInvoked 来监听弹出事件。
PopScope(
  canPop: _myPopDisableEnableLogic(),
  onPopInvoked: (bool didPop) {
    // 处理弹出。如果 `didPop` 为 false,则表示它被阻止了。
  },
)Form.canPop 和 Form.onPopInvoked
#这两个新参数基于 PopScope,并替换了已弃用的 Form.onWillPop 参数。它们与 PopScope 的使用方法与上面相同。
Form(
  canPop: _myPopDisableEnableLogic(),
  onPopInvoked: (bool didPop) {
    // 处理弹出。如果 `didPop` 为 false,则表示它被阻止了。
  },
)Route.popDisposition
#此 getter 同步返回路由的 RoutePopDisposition,它描述了弹出行为。
if (myRoute.popDisposition == RoutePopDisposition.doNotPop) {
  // 返回手势被禁用。
}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: ...
),从 WillPopScope 迁移到 NavigatorPopHandler 以处理嵌套的 Navigators
#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 方法。这已被公开其 canPop 和 onPopInvoked 方法的 PopScope 替换。迁移与上面详细介绍的从 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 来跟踪其窗口小部件子树中 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) {
  // 禁用预测路由转换。
}迁移后的代码:
if (_route.popDisposition == RoutePopDisposition.doNotPop) {
  // 禁用预测路由转换。
}迁移返回确认对话框
#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 文档:
PopScopeNavigatorPopHandlerPopScopeNavigatorPopHandlerPopEntryForm.canPopForm.onPopInvokedRoute.popDispositionModalRoute.registerPopEntryModalRoute.unregisterPopEntry
相关问题:
相关 PR:
除非另有说明,否则本网站上的文档反映的是 Flutter 的最新稳定版本。页面最后更新于 2025-01-30。 查看源代码 或 报告问题。