Migrate RawKeyEvent/RawKeyboard system to KeyEvent/HardwareKeyboard system
Summary
#For some time now (years), Flutter has had two key event systems implemented. The new system reached parity with the old platform-specific raw key event system, and the raw system has been deprecated.
Context
#In the original key event subsystem, handling each platform's quirks in the framework and in client apps caused overly complex code, and the old system didn't properly represent the true state of key events on the system.
                  The legacy API RawKeyboard
                   has been deprecated
                  and will be removed in the future.
                  The HardwareKeyboard
                   and KeyEvent
                   APIs replace this legacy API.
                  An example of this change is FocusNode.onKeyEvent
                  
                  replacing FocusNode.onKey.
                
                  The behavior of RawKeyboard
                   provided a
                  less unified and less regular event model
                  than HardwareKeyboard
                   does.
                  Consider the following examples:
                
- Down events were not always matched with an up event, and vice versa (the set of pressed keys was silently updated).
 - The logical key of the down event was not always the same as that of the up event.
 - Down events and repeat events were not easily distinguishable (had to be tracked manually).
 - Lock modes (such as CapsLock) only had their "enabled" state recorded. There was no way to acquire their pressed state.
 
                  So, the new KeyEvent/HardwareKeyboard-based system was born and, to
                  minimize breaking changes, was implemented in parallel with the old system with
                  the intention of eventually deprecating the raw system. That time has arrived,
                  and application developers should migrate their code to avoid breaking changes
                  that will occur when the deprecated APIs are removed.
                
Description of change
#Below are the APIs that have been deprecated.
Deprecated APIs that have an equivalent
#- 
                    
Focus.onKey=>Focus.onKeyEvent - 
                    
FocusNode.attach'sonKeyargument =>onKeyEventargument - 
                    
FocusNode.onKey=>FocusNode.onKeyEvent - 
                    
FocusOnKeyCallback=>FocusOnKeyEventCallback - 
                    
FocusScope.onKey=>FocusScope.onKeyEvent - 
                    
FocusScopeNode.onKey=>FocusScopeNode.onKeyEvent - 
                    
RawKeyboard=>HardwareKeyboard - 
                    
RawKeyboardListener=>KeyboardListener - 
                    
RawKeyDownEvent=>KeyDownEvent - 
                    
RawKeyEvent=>KeyEvent - 
                    
RawKeyUpEvent=>KeyUpEvent 
APIs that have been discontinued
#These APIs are no longer needed once there is only one key event system, or their functionality is no longer offered.
- 
                    
debugKeyEventSimulatorTransitModeOverride - 
                    
GLFWKeyHelper - 
                    
GtkKeyHelper - 
                    
KeyboardSide - 
                    
KeyDataTransitMode - 
                    
KeyEventManager KeyHelperKeyMessage- 
                    
KeyMessageHandler - 
                    
KeySimulatorTransitModeVariant - 
                    
ModifierKey - 
                    
RawKeyEventData - 
                    
RawKeyEventDataAndroid - 
                    
RawKeyEventDataFuchsia - 
                    
RawKeyEventDataIos - 
                    
RawKeyEventDataLinux - 
                    
RawKeyEventDataMacOs - 
                    
RawKeyEventDataWeb - 
                    
RawKeyEventDataWindows - 
                    
RawKeyEventHandler - 
                    
ServicesBinding.keyEventManager 
Migration guide
#The Flutter framework libraries have already been migrated. If your code uses any of the classes or methods listed in the previous section, migrate to these new APIs.
Migrating your code that uses RawKeyEvent
                  #
                
                  For the most part, there are equivalent KeyEvent APIs available for all of the
                  RawKeyEvent APIs.
                
                  Some APIs relating to platform specific information contained in
                  RawKeyEventData
                   objects or their subclasses have been removed and are no
                  longer supported. One exception is that RawKeyEventDataAndroid.eventSource
                  
                  information is accessible now as KeyEvent.deviceType
                   in a more
                  platform independent form.
                
Migrating isKeyPressed and related functions
                  #
                
                  If the legacy code used the RawKeyEvent.isKeyPressed,
                  RawKeyEvent.isControlPressed, 
                  RawKeyEvent.isShiftPressed,
                  RawKeyEvent.isAltPressed, or 
                  RawKeyEvent.isMetaPressed
                   APIs, there
                  are now equivalent functions on the HardwareKeyboard
                   singleton instance,
                  but are not available on [KeyEvent]. RawKeyEvent.isKeyPressed
                   is available
                  as HardwareKeyboard.isLogicalKeyPressed.
                
Before:
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;
}
                    
                    
                    
                  After:
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;
}
                    
                    
                    
                  Setting onKey for focus
                  #
                
                  If the legacy code was using the Focus.onKey, 
                  FocusScope.onKey,
                  FocusNode.onKey, or 
                  FocusScopeNode.onKey
                   parameters, then there is
                  an equivalent Focus.onKeyEvent, 
                  FocusScope.onKeyEvent,
                  FocusNode.onKeyEvent, or 
                  FocusScopeNode.onKeyEvent
                   parameter that
                  supplies KeyEvents instead of RawKeyEvents.
                
Before:
Widget build(BuildContext context) {
  return Focus(
    onKey: (RawKeyEvent keyEvent) {
      print('Key event: $keyEvent');
      return KeyEventResult.ignored;
    }
    child: child,
  );
}
                    
                    
                    
                  After:
Widget build(BuildContext context) {
  return Focus(
    onKeyEvent: (KeyEvent keyEvent) {
      print('Key event: $keyEvent');
      return KeyEventResult.ignored;
    }
    child: child,
  );
}
                    
                    
                    
                  Repeat key event handling
#
                  If you were relying on the RawKeyEvent.repeat
                   attribute to determine if a
                  key was a repeated key event, that has now been separated into a separate
                  KeyRepeatEvent
                   type.
                
Before:
KeyEventResult _handleKeyEvent(RawKeyEvent keyEvent) {
  if (keyEvent is RawKeyDownEvent) {
    print('Key down: ${keyEvent.data.logicalKey.keyLabel}(${keyEvent.repeat ? ' (repeated)' : ''})');
  }
  return KeyEventResult.ignored;
}
                    
                    
                    
                  After:
KeyEventResult _handleKeyEvent(KeyEvent _) {
  if (keyEvent is KeyDownEvent || keyEvent is KeyRepeatEvent) {
    print('Key down: ${keyEvent.logicalKey.keyLabel}(${keyEvent is KeyRepeatEvent ? ' (repeated)' : ''})');
  }
  return KeyEventResult.ignored;
}
                    
                    
                    
                  
                  Though it is not a subclass of KeyDownEvent,
                  a KeyRepeatEvent
                   is also a key down event.
                  Don't assume that keyEvent is! KeyDownEvent only allows key up events.
                  Check both KeyDownEvent and KeyRepeatEvent.
                
Timeline
#
                  Landed in version: 3.18.0-7.0.pre
                  In stable release: 3.19.0
                
References
#Replacement API documentation:
- 
                    
Focus.onKeyEvent - 
                    
FocusNode.onKeyEvent - 
                    
FocusOnKeyEventCallback - 
                    
FocusScope.onKeyEvent - 
                    
FocusScopeNode.onKeyEvent - 
                    
HardwareKeyboard - 
                    
KeyboardListener - 
                    
KeyDownEvent - 
                    
KeyRepeatEvent KeyEvent- 
                    
KeyEventHandler KeyUpEvent
Relevant issues:
Relevant PRs:
Unless stated otherwise, the documentation on this site reflects Flutter 3.35.5. Page last updated on 2025-10-28. View source or report an issue.