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.
Original behavior
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.
New behavior
Below are the APIs that have been deprecated.
- Who is affected
- Applications and packages relying on the previous behaviour or signature. Code that uses RawKeyboard, HardwareKeyboard, KeyEvent and related APIs.
- Practical impact
- Behaviour or API differences that can break builds or change runtime behaviour.
- What action should I take?
- Follow the official migration guide below, update affected call sites, run `dart fix --apply` where a fix is available, then run your tests.
Before and after (from the official guide)
| 1 | − | KeyEventResult _handleKeyEvent(RawKeyEvent keyEvent) { |
| 2 | − | if (keyEvent.isControlPressed || |
| 3 | − | keyEvent.isShiftPressed || |
| 4 | − | keyEvent.isAltPressed || |
| 5 | − | keyEvent.isMetaPressed) { |
| 6 | print('Modifier pressed: $keyEvent'); | |
| 7 | } | |
| 8 | − | if (keyEvent.isKeyPressed(LogicalKeyboardKey.keyA)) { |
| 9 | print('Key A pressed.'); | |
| 10 | } | |
| 11 | return KeyEventResult.ignored; | |
| 12 | } |
| 1 | + | KeyEventResult _handleKeyEvent(KeyEvent _) { |
| 2 | + | if (HardwareKeyboard.instance.isControlPressed || |
| 3 | + | HardwareKeyboard.instance.isShiftPressed || |
| 4 | + | HardwareKeyboard.instance.isAltPressed || |
| 5 | + | HardwareKeyboard.instance.isMetaPressed) { |
| 6 | print('Modifier pressed: $keyEvent'); | |
| 7 | } | |
| 8 | + | if (HardwareKeyboard.instance.isLogicalKeyPressed(LogicalKeyboardKey.keyA)) { |
| 9 | print('Key A pressed.'); | |
| 10 | } | |
| 11 | return KeyEventResult.ignored; | |
| 12 | } |
Raw before / after
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;
}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;
}Official migration guide (excerpt, © The Flutter Authors, CC BY 3.0)
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.
AI_PROVIDER (for example to openai-compatible with a local Ollama or LM Studio endpoint) to enable grounded explanations. Everything else on this page works without an AI provider.Listed in the official breaking-changes index (docs.flutter.dev/release/breaking-changes).
An official announcement or migration guide documents an intended behaviour or API change. A verified source change (git diff) shows code changed but does not by itself prove a public API is breaking. AI interpretations are labelled and never used to classify a change.
- Official migration guide
- Focus.onKeyEvent
- FocusNode.onKeyEvent
- FocusOnKeyEventCallback
- FocusScope.onKeyEvent
- FocusScopeNode.onKeyEvent
- HardwareKeyboard
- KeyboardListener
- KeyDownEvent
- KeyRepeatEvent
- KeyEvent
- KeyEventHandler
- KeyUpEvent
- RawKeyEvent and RawKeyboard, et al should be deprecated and removed (Issue 136419)
- Deprecate RawKeyEvent, et al. and exempt uses in the framework.
- Source commit
- b3045439b7c0
- Source last updated
- Oct 21, 2026
- Last synchronized
- Oct 21, 2026
- Publication status
- published
Publication history
- Oct 21, 2026, 8:31 PM: new → published (ingestion) — Backed by an official breaking-change/migration guide.
AI_PROVIDER (for example to openai-compatible with a local Ollama or LM Studio endpoint) to enable grounded explanations. Everything else on this page works without an AI provider.