跳至主要內容

將 RawKeyEvent/RawKeyboard 系統遷移至 KeyEvent/HardwareKeyboard 系統

摘要

#

一段時間以來 (數年),Flutter 實作了兩個按鍵事件系統。新的系統已達到與舊的平台特定原始按鍵事件系統相同的效能,而原始系統已遭到淘汰。

背景

#

在原始的按鍵事件子系統中,在框架和用戶端應用程式中處理每個平台的怪癖會導致程式碼過於複雜,而且舊的系統無法正確表示系統上按鍵事件的真實狀態。

舊版 API RawKeyboard 已被棄用,未來將會移除。新的 HardwareKeyboardKeyEvent API 將取代這個舊版 API。舉例來說,FocusNode.onKeyEvent 取代了 FocusNode.onKey

RawKeyboard 的行為提供的事件模型,相較於 HardwareKeyboard 來說,較不統一且不規律。請參考以下範例:

  • 按下事件不一定會與放開事件配對,反之亦然(已按下的按鍵集合會靜默地更新)。
  • 按下事件的邏輯按鍵不一定與放開事件的邏輯按鍵相同。
  • 按下事件和重複事件不容易區分(必須手動追蹤)。
  • 鎖定模式(例如 CapsLock)只會記錄其「啟用」狀態。無法取得它們的按下狀態。

因此,新的基於 KeyEvent/HardwareKeyboard 的系統誕生了,並且為了盡量減少破壞性變更,與舊系統並行實作,並打算最終棄用舊的系統。現在時機已到,應用程式開發人員應遷移其程式碼,以避免在棄用的 API 移除時發生破壞性變更。

變更說明

#

以下是已被棄用的 API:

具有對應項目的已淘汰 API

#

已停止使用的 API

#

一旦只有一個按鍵事件系統,或不再提供其功能時,這些 API 將不再需要。

遷移指南

#

Flutter framework 函式庫已完成遷移。如果您的程式碼使用上一節列出的任何類別或方法,請遷移至這些新的 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;
}

雖然它不是 KeyDownEvent 的子類別,但 KeyRepeatEvent 也是一個按鍵按下事件。請勿假設 keyEvent is! KeyDownEvent 只允許按鍵放開事件。請檢查 KeyDownEventKeyRepeatEvent

時程表

#

於版本中發布:3.18.0-7.0.pre
於穩定版本中發布:3.19.0

參考資料

#

替換 API 文件

相關議題

相關 PR