Repository: flutter
Issue: ☂️ Embedding keyboard states are not reset after hot restart
Labels: a: text input,c: crash,platform-android,platform-ios,P2,c: fatal crash,team-android,triaged-android

We should add a hook to `onPreEngineRestart` that frees and recreates the platform keyboard manager. Since the keyboard manager is stateful, failing to reset its state can cause state instability or even crash the app after a hot restart (Shift-R in CLI.)


## Progress

- [x] Embedder: https://github.com/flutter/engine/pull/28924
- [x] Linux
- [x] Windows
- [x] macOS
- [ ] iOS
- [ ] Android

## Reproduction
### Linux 

Reproduction, on an app with only a text field:
1. Start the app
2. Alt-Tab to the terminal
3. Hot restart
4. Switch back to the app.

There are two unexpected behaviors:

1. The text field no longer displays texts. (This behavior will disappear after another hot restart.)
2. The CLI reports the crash log. (This behavior persists after another hot restart.)

<details><summary>logs</summary><pre>
══╡ EXCEPTION CAUGHT BY SERVICES LIBRARY ╞══════════════════════════════════════════════════════════
The following assertion was thrown during a platform message callback:
A KeyUpEvent is dispatched, but the state shows that the physical key is not pressed. If this occurs
in real application, please report this bug to Flutter. If this occurs in unit tests, please ensure
that simulated events follow Flutter's event model as documented in `HardwareKeyboard`. This was the
event: KeyUpEvent#6ab5b(physicalKey: PhysicalKeyboardKey#700e2(usbHidUsage: "0x000700e2", debugName:
"Alt Left"), logicalKey: LogicalKeyboardKey#00104(keyId: "0x200000104", keyLabel: "Alt Left",
debugName: "Alt Left"), character: null, timeStamp: 1:27:37.409000, synthesized)
'package:flutter/src/services/hardware_keyboard.dart':
Failed assertion: line 441 pos 16: '_pressedKeys.containsKey(event.physicalKey)'

Either the assertion indicates an error in the framework itself, or we should provide substantially
more information in this error message to help you determine and fix the underlying cause.
In either case, please report this assertion by filing a bug on GitHub:
  https://github.com/flutter/flutter/issues/new?template=2_bug.md

When the exception was thrown, this was the stack:
#2      HardwareKeyboard._assertEventIsRegular.<anonymous closure> (package:flutter/src/services/hardware_keyboard.dart:441:16)
#3      HardwareKeyboard._assertEventIsRegular (package:flutter/src/services/hardware_keyboard.dart:452:6)
#4      HardwareKeyboard.handleKeyEvent (package:flutter/src/services/hardware_keyboard.dart:543:5)
#5      KeyEventManager.handleRawKeyMessage (package:flutter/src/services/hardware_keyboard.dart:821:35)
#6      BasicMessageChannel.setMessageHandler.<anonymous closure> (package:flutter/src/services/platform_channel.dart:73:49)
#7      BasicMessageChannel.setMessageHandler.<anonymous closure> (package:flutter/src/services/platform_channel.dart:72:47)
#8      _DefaultBinaryMessenger.setMessageHandler.<anonymous closure> (package:flutter/src/services/binding.dart:379:35)
#9      _DefaultBinaryMessenger.setMessageHandler.<anonymous closure> (package:flutter/src/services/binding.dart:376:46)
#10     _invoke2.<anonymous closure> (dart:ui/hooks.dart:205:15)
#14     _invoke2 (dart:ui/hooks.dart:204:10)
#15     _ChannelCallbackRecord.invoke (dart:ui/channel_buffers.dart:42:5)
#16     _Channel.push (dart:ui/channel_buffers.dart:132:31)
#17     ChannelBuffers.push (dart:ui/channel_buffers.dart:329:17)
#18     PlatformDispatcher._dispatchPlatformMessage (dart:ui/platform_dispatcher.dart:544:22)
#19     _dispatchPlatformMessage (dart:ui/hooks.dart:92:31)
(elided 5 frames from class _AssertionError and dart:async)
════════════════════════════════════════════════════════════════════════════════════════════════════
</pre></details>

This should be fixed by https://github.com/flutter/engine/pull/28877.

### Windows

Reproduction, on an app with only a text field:
1. Start a Flutter app with a single text field, and use German keyboard layout
2. Press AltRight-Tab to the CLI.
3. This will cause the Alt-Tab menu stuck, and it's not related to Flutter. Press AltRight and CtrlLeft again.
4. Press Shift-R to hot restart.

The CLI will print a crash.

<details><summary>logs</summary><pre>
══╡ EXCEPTION CAUGHT BY SERVICES LIBRARY ╞══════════════════════════════════════════════════════════
The following assertion was thrown during a platform message callback:
A KeyUpEvent is dispatched, but the state shows that the physical key is not pressed. If this occurs
in real application, please report this bug to Flutter. If this occurs in unit tests, please ensure
that simulated events follow Flutter's event model as documented in `HardwareKeyboard`. This was the
event: KeyUpEvent#13fab(physicalKey: PhysicalKeyboardKey#700e0(usbHidUsage: "0x000700e0", debugName:
"Control Left"), logicalKey: LogicalKeyboardKey#00100(keyId: "0x200000100", keyLabel: "Control
Left", debugName: "Control Left"), character: null, timeStamp: 220:13:11.679805, synthesized)
'package:flutter/src/services/hardware_keyboard.dart':
Failed assertion: line 441 pos 16: '_pressedKeys.containsKey(event.physicalKey)'

Either the assertion indicates an error in the framework itself, or we should provide substantially
more information in this error message to help you determine and fix the underlying cause.
In either case, please report this assertion by filing a bug on GitHub:
  https://github.com/flutter/flutter/issues/new?template=2_bug.md

When the exception was thrown, this was the stack:
#2      HardwareKeyboard._assertEventIsRegular.<anonymous closure> (package:flutter/src/services/hardware_keyboard.dart:441:16)
#3      HardwareKeyboard._assertEventIsRegular (package:flutter/src/services/hardware_keyboard.dart:452:6)
#4      HardwareKeyboard.handleKeyEvent (package:flutter/src/services/hardware_keyboard.dart:543:5)
#5      KeyEventManager.handleRawKeyMessage (package:flutter/src/services/hardware_keyboard.dart:821:35)
#6      BasicMessageChannel.setMessageHandler.<anonymous closure> (package:flutter/src/services/platform_channel.dart:77:49)
#7      BasicMessageChannel.setMessageHandler.<anonymous closure> (package:flutter/src/services/platform_channel.dart:76:47)
#8      _DefaultBinaryMessenger.setMessageHandler.<anonymous closure> (package:flutter/src/services/binding.dart:386:35)
#9      _DefaultBinaryMessenger.setMessageHandler.<anonymous closure> (package:flutter/src/services/binding.dart:383:46)
#10     _invoke2.<anonymous closure> (dart:ui/hooks.dart:189:15)
#14     _invoke2 (dart:ui/hooks.dart:188:10)
#15     _ChannelCallbackRecord.invoke (dart:ui/channel_buffers.dart:42:5)
#16     _Channel.push (dart:ui/channel_buffers.dart:132:31)
#17     ChannelBuffers.push (dart:ui/channel_buffers.dart:329:17)
#18     PlatformDispatcher._dispatchPlatformMessage (dart:ui/platform_dispatcher.dart:544:22)
#19     _dispatchPlatformMessage (dart:ui/hooks.dart:83:31)
(elided 5 frames from class _AssertionError and dart:async)
════════════════════════════════════════════════════════════════════════════════════════════════════
Lost connection to device.
</pre></details>

This should be fixed by https://github.com/flutter/engine/pull/28970.