概述

#

长期以来(数年),Flutter 实现了两个按键事件系统。新系统已与旧的特定于平台的原始按键事件系统达到同等水平,并且原始系统已被弃用。

背景

#

在原始的按键事件子系统中,处理框架和客户端应用程序中每个平台的怪癖导致代码过于复杂,并且旧系统未能正确表示系统上按键事件的真实状态。

旧版 API RawKeyboard 已被弃用,并将于未来移除。HardwareKeyboardKeyEvent API 将取代此旧版 API。此更改的一个示例是 FocusNode.onKeyEvent 取代了 FocusNode.onKey

RawKeyboard 的行为提供了一个比 HardwareKeyboard 更不统一、更不规则的事件模型。请考虑以下示例:

  • 按下事件并非总是与抬起事件匹配,反之亦然(已按下的键集被静默更新)。
  • 按下事件的逻辑键并非总是与抬起事件的逻辑键相同。
  • 按下事件和重复事件不容易区分(必须手动跟踪)。
  • 锁定模式(例如大写锁定)只记录了它们的“启用”状态。无法获取它们的按下状态。

因此,新的基于 KeyEvent/HardwareKeyboard 的系统应运而生,为了最大程度地减少破坏性更改,它与旧系统并行实现,旨在最终弃用原始系统。现在是时候了,应用程序开发者应迁移其代码,以避免在移除弃用 API 时可能发生的破坏性更改。

变更说明

#

以下是已弃用的 API。

已弃用但有等效项的 API

#

已停用的 API

#

一旦只有一个按键事件系统,或者其功能不再提供,这些 API 将不再需要。

迁移指南

#

Flutter 框架库已经完成迁移。如果您的代码使用了上一节中列出的任何类或方法,请迁移到这些新的 API。

迁移使用 RawKeyEvent 的代码

#

在大多数情况下,所有 RawKeyEvent API 都有等效的 KeyEvent API 可用。

RawKeyEventData 对象或其子类中包含的平台特定信息相关的一些 API 已被移除,不再受支持。一个例外是 RawKeyEventDataAndroid.eventSource 信息现在可以以更独立于平台的形式通过 KeyEvent.deviceType 访问。

#

如果旧代码使用了 RawKeyEvent.isKeyPressedRawKeyEvent.isControlPressedRawKeyEvent.isShiftPressedRawKeyEvent.isAltPressedRawKeyEvent.isMetaPressed API,现在 HardwareKeyboard 单例实例上提供了等效函数,但 [KeyEvent] 上不可用。RawKeyEvent.isKeyPressed 现在可作为 HardwareKeyboard.isLogicalKeyPressed 使用。

之前

dart
KeyEventResult _handleKeyEvent(RawKeyEvent keyEvent) {
  if (keyEvent.isControlPressed ||
      keyEvent.isShiftPressed ||
      keyEvent.isAltPressed ||
      keyEvent.isMetaPressed) {
    print('Modifier pressed: $keyEvent');
  }
  if (keyEvent.isKeyPressed(LogicalKeyboardKey.keyA)) {
    print('Key A pressed.');
  }
  return KeyEventResult.ignored;
}

之后

dart
KeyEventResult _handleKeyEvent(KeyEvent _) {
  if (HardwareKeyboard.instance.isControlPressed ||
      HardwareKeyboard.instance.isShiftPressed ||
      HardwareKeyboard.instance.isAltPressed ||
      HardwareKeyboard.instance.isMetaPressed) {
    print('Modifier pressed: $keyEvent');
  }
  if (HardwareKeyboard.instance.isLogicalKeyPressed(LogicalKeyboardKey.keyA)) {
    print('Key A pressed.');
  }
  return KeyEventResult.ignored;
}

为焦点设置 onKey

#

如果旧代码使用了 Focus.onKeyFocusScope.onKeyFocusNode.onKeyFocusScopeNode.onKey 参数,那么现在有一个等效的 Focus.onKeyEventFocusScope.onKeyEventFocusNode.onKeyEventFocusScopeNode.onKeyEvent 参数,它提供 KeyEvent 而不是 RawKeyEvent

之前

dart
Widget build(BuildContext context) {
  return Focus(
    onKey: (RawKeyEvent keyEvent) {
      print('Key event: $keyEvent');
      return KeyEventResult.ignored;
    }
    child: child,
  );
}

之后

dart
Widget build(BuildContext context) {
  return Focus(
    onKeyEvent: (KeyEvent keyEvent) {
      print('Key event: $keyEvent');
      return KeyEventResult.ignored;
    }
    child: child,
  );
}

重复按键事件处理

#

如果您曾依赖 RawKeyEvent.repeat 属性来判断某个键是否为重复按键事件,现在这已分离为一个独立的 KeyRepeatEvent 类型。

之前

dart
KeyEventResult _handleKeyEvent(RawKeyEvent keyEvent) {
  if (keyEvent is RawKeyDownEvent) {
    print('Key down: ${keyEvent.data.logicalKey.keyLabel}(${keyEvent.repeat ? ' (repeated)' : ''})');
  }
  return KeyEventResult.ignored;
}

之后

dart
KeyEventResult _handleKeyEvent(KeyEvent _) {
  if (keyEvent is KeyDownEvent || keyEvent is KeyRepeatEvent) {
    print('Key down: ${keyEvent.logicalKey.keyLabel}(${keyEvent is KeyRepeatEvent ? ' (repeated)' : ''})');
  }
  return KeyEventResult.ignored;
}

尽管 KeyRepeatEvent 不是 KeyDownEvent 的子类,但它也是一个按键按下事件。不要假定 keyEvent is! KeyDownEvent 只允许按键抬起事件。请同时检查 KeyDownEventKeyRepeatEvent

时间线

#

发布版本:3.18.0-7.0.pre
稳定版本:3.19.0

参考资料

#

替换 API 文档

相关问题

相关 PR