Skip to content
Boring Doc
BreakingHigh impactOfficial migration guidesince 3.19.0landed 3.18.0-7.0.pre

Migrate RawKeyEvent/RawKeyboard system to KeyEvent/HardwareKeyboard system

Framework · Official source

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)

dart+6−6
Before
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 }
After
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.

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.

Migration assistantAI-generated · grounded in cited sources
AI explanations are not configured
Set 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.
Official migration guide·Verified classification (95%)

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.

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 explanationAI-generated · grounded in cited sources
AI explanations are not configured
Set 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.
Was this useful?
Migrate RawKeyEvent/RawKeyboard system to KeyEvent/HardwareKeyboard system · Boring Doc