[RawKeyboard] Allow inconsistent modifiers for iOS and Android (#108926)
diff --git a/packages/flutter/lib/src/services/raw_keyboard.dart b/packages/flutter/lib/src/services/raw_keyboard.dart
index 894ba17..0874f1d 100644
--- a/packages/flutter/lib/src/services/raw_keyboard.dart
+++ b/packages/flutter/lib/src/services/raw_keyboard.dart
@@ -836,10 +836,13 @@
// exist in the modifier list. Enforce the pressing state.
if (event is RawKeyDownEvent && thisKeyModifier != null
&& !_keysPressed.containsKey(event.physicalKey)) {
- // So far this inconsistancy is only found on Linux GTK for AltRight in a
- // rare case. (See https://github.com/flutter/flutter/issues/93278 .) In
- // other cases, this inconsistancy will be caught by an assertion later.
- if (event.data is RawKeyEventDataLinux && event.physicalKey == PhysicalKeyboardKey.altRight) {
+ // This inconsistancy is found on Linux GTK for AltRight:
+ // https://github.com/flutter/flutter/issues/93278
+ // And also on Android and iOS:
+ // https://github.com/flutter/flutter/issues/101090
+ if ((event.data is RawKeyEventDataLinux && event.physicalKey == PhysicalKeyboardKey.altRight)
+ || event.data is RawKeyEventDataIos
+ || event.data is RawKeyEventDataAndroid) {
final LogicalKeyboardKey? logicalKey = _allModifiersExceptFn[event.physicalKey];
if (logicalKey != null) {
_keysPressed[event.physicalKey] = logicalKey;
diff --git a/packages/flutter/test/services/raw_keyboard_test.dart b/packages/flutter/test/services/raw_keyboard_test.dart
index 5cc53d8..5dcb783 100644
--- a/packages/flutter/test/services/raw_keyboard_test.dart
+++ b/packages/flutter/test/services/raw_keyboard_test.dart
@@ -759,12 +759,11 @@
} else {
keyEventMessage = const <String, dynamic>{
'type': 'keydown',
- 'keymap': 'android',
- 'keyCode': 0x3b, // Left shift key keyCode
- 'scanCode': 0x2a,
- 'metaState': 0x0, // No shift key metaState set!
- 'source': 0x101,
- 'deviceId': 1,
+ 'keymap': 'windows',
+ 'keyCode': 16, // Left shift key keyCode
+ 'characterCodePoint': 0,
+ 'scanCode': 42,
+ 'modifiers': 0x0, // No shift key metaState set!
};
}
@@ -784,6 +783,65 @@
);
});
+ testWidgets('Allows inconsistent modifier for iOS', (WidgetTester _) async {
+ // Use `testWidgets` for clean-ups.
+ final List<RawKeyEvent> events = <RawKeyEvent>[];
+ RawKeyboard.instance.addListener(events.add);
+ addTearDown(() {
+ RawKeyboard.instance.removeListener(events.add);
+ });
+ await TestDefaultBinaryMessengerBinding.instance!.defaultBinaryMessenger.handlePlatformMessage(
+ SystemChannels.keyEvent.name,
+ SystemChannels.keyEvent.codec.encodeMessage(const <String, dynamic>{
+ 'type': 'keydown',
+ 'keymap': 'ios',
+ 'keyCode': 0x00000039,
+ 'characters': '',
+ 'charactersIgnoringModifiers': '',
+ 'modifiers': 0,
+ }),
+ (ByteData? data) { },
+ );
+
+ expect(events, hasLength(1));
+ final RawKeyEvent capsLockKey = events[0];
+ final RawKeyEventDataIos data = capsLockKey.data as RawKeyEventDataIos;
+ expect(data.physicalKey, equals(PhysicalKeyboardKey.capsLock));
+ expect(data.logicalKey, equals(LogicalKeyboardKey.capsLock));
+ expect(RawKeyboard.instance.keysPressed, contains(LogicalKeyboardKey.capsLock));
+ }, skip: isBrowser); // [intended] This is an iOS-specific group.
+
+ testWidgets('Allows inconsistent modifier for Android', (WidgetTester _) async {
+ // Use `testWidgets` for clean-ups.
+ final List<RawKeyEvent> events = <RawKeyEvent>[];
+ RawKeyboard.instance.addListener(events.add);
+ addTearDown(() {
+ RawKeyboard.instance.removeListener(events.add);
+ });
+ await TestDefaultBinaryMessengerBinding.instance!.defaultBinaryMessenger.handlePlatformMessage(
+ SystemChannels.keyEvent.name,
+ SystemChannels.keyEvent.codec.encodeMessage(const <String, dynamic>{
+ 'type': 'keydown',
+ 'keymap': 'android',
+ 'keyCode': 115,
+ 'plainCodePoint': 0,
+ 'codePoint': 0,
+ 'scanCode': 58,
+ 'metaState': 0,
+ 'source': 0x101,
+ 'deviceId': 1,
+ }),
+ (ByteData? data) { },
+ );
+
+ expect(events, hasLength(1));
+ final RawKeyEvent capsLockKey = events[0];
+ final RawKeyEventDataAndroid data = capsLockKey.data as RawKeyEventDataAndroid;
+ expect(data.physicalKey, equals(PhysicalKeyboardKey.capsLock));
+ expect(data.logicalKey, equals(LogicalKeyboardKey.capsLock));
+ expect(RawKeyboard.instance.keysPressed, contains(LogicalKeyboardKey.capsLock));
+ }, skip: isBrowser); // [intended] This is an Android-specific group.
+
testWidgets('Dispatch events to all handlers', (WidgetTester tester) async {
final FocusNode focusNode = FocusNode();
final List<int> logs = <int>[];