Fix the `character` field of the `RawKeyEvent` to hold correct data on non-Android platforms. (#65667)
This fixes a problem where the character field of the RawKeyEvent was not being set at all for non-Android platforms.
I also updated the key maps, and corrected a problem with the Windows key map where the backquote character wasn't correctly mapped.
diff --git a/dev/manual_tests/lib/raw_keyboard.dart b/dev/manual_tests/lib/raw_keyboard.dart
index 5c50210..e08b260 100644
--- a/dev/manual_tests/lib/raw_keyboard.dart
+++ b/dev/manual_tests/lib/raw_keyboard.dart
@@ -79,6 +79,7 @@
final String modifierList = data.modifiersPressed.keys.map<String>(_getEnumName).join(', ').replaceAll('Modifier', '');
final List<Widget> dataText = <Widget>[
Text('${_event.runtimeType}'),
+ if (_event.character?.isNotEmpty ?? false) Text('character produced: "${_event.character}"'),
Text('modifiers set: $modifierList'),
];
if (data is RawKeyEventDataAndroid) {
@@ -114,6 +115,10 @@
dataText.add(Text('scanCode: ${data.scanCode}'));
dataText.add(Text('characterCodePoint: ${data.characterCodePoint}'));
dataText.add(Text('modifiers: ${data.modifiers} (${_asHex(data.modifiers)})'));
+ } else if (data is RawKeyEventDataWeb) {
+ dataText.add(Text('key: ${data.key}'));
+ dataText.add(Text('code: ${data.code}'));
+ dataText.add(Text('metaState: ${data.metaState} (${_asHex(data.metaState)})'));
}
dataText.add(Text('logical: ${_event.logicalKey}'));
dataText.add(Text('physical: ${_event.physicalKey}'));
diff --git a/dev/tools/gen_keycodes/data/key_data.json b/dev/tools/gen_keycodes/data/key_data.json
index b68fc8a..6ee1488 100644
--- a/dev/tools/gen_keycodes/data/key_data.json
+++ b/dev/tools/gen_keycodes/data/key_data.json
@@ -2472,7 +2472,9 @@
"gtk": [
"quoteleft"
],
- "windows": null
+ "windows": [
+ "OEM_3"
+ ]
},
"scanCodes": {
"android": [
@@ -2494,7 +2496,9 @@
"gtk": [
96
],
- "windows": null
+ "windows": [
+ 192
+ ]
}
},
"comma": {
@@ -6565,7 +6569,9 @@
"windows": null
},
"scanCodes": {
- "android": null,
+ "android": [
+ 370
+ ],
"usb": 786529,
"linux": 370,
"xkb": 378,
@@ -6822,7 +6828,9 @@
"windows": null
},
"scanCodes": {
- "android": null,
+ "android": [
+ 405
+ ],
"usb": 786563,
"linux": 405,
"xkb": 413,
@@ -7891,7 +7899,9 @@
"windows": null
},
"scanCodes": {
- "android": null,
+ "android": [
+ 583
+ ],
"usb": 786891,
"linux": 583,
"xkb": 591,
diff --git a/dev/tools/gen_keycodes/data/key_name_to_windows_name.json b/dev/tools/gen_keycodes/data/key_name_to_windows_name.json
index 2513e1d..921ca87 100644
--- a/dev/tools/gen_keycodes/data/key_name_to_windows_name.json
+++ b/dev/tools/gen_keycodes/data/key_name_to_windows_name.json
@@ -132,7 +132,7 @@
"minus": ["OEM_MINUS"],
"period": ["OEM_PERIOD"],
"slash": ["OEM_2"],
- "backQuote": ["OEM_3"],
+ "backquote": ["OEM_3"],
"gamepadA": ["GAMEPAD_A"],
"gamepadB": ["GAMEPAD_B"],
"gamepadX": ["GAMEPAD_X"],
diff --git a/dev/tools/gen_keycodes/data/keyboard_maps.tmpl b/dev/tools/gen_keycodes/data/keyboard_maps.tmpl
index 182ebbf..498f636 100644
--- a/dev/tools/gen_keycodes/data/keyboard_maps.tmpl
+++ b/dev/tools/gen_keycodes/data/keyboard_maps.tmpl
@@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-// @dart = 2.8
-
// DO NOT EDIT -- DO NOT EDIT -- DO NOT EDIT
// This file is generated by dev/tools/gen_keycodes/bin/gen_keycodes.dart and
// should not be edited directly.
diff --git a/packages/flutter/lib/src/services/keyboard_maps.dart b/packages/flutter/lib/src/services/keyboard_maps.dart
index 0126631..c73c14d 100644
--- a/packages/flutter/lib/src/services/keyboard_maps.dart
+++ b/packages/flutter/lib/src/services/keyboard_maps.dart
@@ -2,7 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-
// DO NOT EDIT -- DO NOT EDIT -- DO NOT EDIT
// This file is generated by dev/tools/gen_keycodes/bin/gen_keycodes.dart and
// should not be edited directly.
@@ -349,8 +348,10 @@
100: PhysicalKeyboardKey.altRight,
126: PhysicalKeyboardKey.metaRight,
358: PhysicalKeyboardKey.info,
+ 370: PhysicalKeyboardKey.closedCaptionToggle,
225: PhysicalKeyboardKey.brightnessUp,
224: PhysicalKeyboardKey.brightnessDown,
+ 405: PhysicalKeyboardKey.mediaLast,
174: PhysicalKeyboardKey.exit,
402: PhysicalKeyboardKey.channelUp,
403: PhysicalKeyboardKey.channelDown,
@@ -372,6 +373,7 @@
215: PhysicalKeyboardKey.launchMail,
429: PhysicalKeyboardKey.launchContacts,
397: PhysicalKeyboardKey.launchCalendar,
+ 583: PhysicalKeyboardKey.launchAssistant,
181: PhysicalKeyboardKey.newKey,
160: PhysicalKeyboardKey.close,
206: PhysicalKeyboardKey.close,
@@ -2290,6 +2292,7 @@
220: LogicalKeyboardKey.backslash,
186: LogicalKeyboardKey.semicolon,
222: LogicalKeyboardKey.quote,
+ 192: LogicalKeyboardKey.backquote,
188: LogicalKeyboardKey.comma,
190: LogicalKeyboardKey.period,
191: LogicalKeyboardKey.slash,
diff --git a/packages/flutter/lib/src/services/raw_keyboard.dart b/packages/flutter/lib/src/services/raw_keyboard.dart
index 46e49dd..a8a665e 100644
--- a/packages/flutter/lib/src/services/raw_keyboard.dart
+++ b/packages/flutter/lib/src/services/raw_keyboard.dart
@@ -267,6 +267,7 @@
/// on the [SystemChannels.keyEvent] channel.
factory RawKeyEvent.fromMessage(Map<String, dynamic> message) {
RawKeyEventData data;
+ String? character;
final String keymap = message['keymap'] as String;
switch (keymap) {
@@ -284,13 +285,20 @@
deviceId: message['deviceId'] as int? ?? 0,
repeatCount: message['repeatCount'] as int? ?? 0,
);
+ if (message.containsKey('character')) {
+ character = message['character'] as String?;
+ }
break;
case 'fuchsia':
+ final int codePoint = message['codePoint'] as int? ?? 0;
data = RawKeyEventDataFuchsia(
hidUsage: message['hidUsage'] as int? ?? 0,
- codePoint: message['codePoint'] as int? ?? 0,
+ codePoint: codePoint,
modifiers: message['modifiers'] as int? ?? 0,
);
+ if (codePoint != 0) {
+ character = String.fromCharCode(codePoint);
+ }
break;
case 'macos':
data = RawKeyEventDataMacOs(
@@ -298,15 +306,20 @@
charactersIgnoringModifiers: message['charactersIgnoringModifiers'] as String? ?? '',
keyCode: message['keyCode'] as int? ?? 0,
modifiers: message['modifiers'] as int? ?? 0);
+ character = message['characters'] as String?;
break;
case 'linux':
+ final int unicodeScalarValues = message['unicodeScalarValues'] as int? ?? 0;
data = RawKeyEventDataLinux(
keyHelper: KeyHelper(message['toolkit'] as String? ?? ''),
- unicodeScalarValues: message['unicodeScalarValues'] as int? ?? 0,
+ unicodeScalarValues: unicodeScalarValues,
keyCode: message['keyCode'] as int? ?? 0,
scanCode: message['scanCode'] as int? ?? 0,
modifiers: message['modifiers'] as int? ?? 0,
isDown: message['type'] == 'keydown');
+ if (unicodeScalarValues != 0) {
+ character = String.fromCharCode(unicodeScalarValues);
+ }
break;
case 'web':
data = RawKeyEventDataWeb(
@@ -314,14 +327,19 @@
key: message['key'] as String? ?? '',
metaState: message['metaState'] as int? ?? 0,
);
+ character = message['key'] as String?;
break;
case 'windows':
+ final int characterCodePoint = message['characterCodePoint'] as int? ?? 0;
data = RawKeyEventDataWindows(
keyCode: message['keyCode'] as int? ?? 0,
scanCode: message['scanCode'] as int? ?? 0,
- characterCodePoint: message['characterCodePoint'] as int? ?? 0,
+ characterCodePoint: characterCodePoint,
modifiers: message['modifiers'] as int? ?? 0,
);
+ if (characterCodePoint != 0) {
+ character = String.fromCharCode(characterCodePoint);
+ }
break;
default:
// Raw key events are not yet implemented on iOS or other platforms,
@@ -333,7 +351,7 @@
final String type = message['type'] as String;
switch (type) {
case 'keydown':
- return RawKeyDownEvent(data: data, character: message['character'] as String);
+ return RawKeyDownEvent(data: data, character: character);
case 'keyup':
return RawKeyUpEvent(data: data);
default:
diff --git a/packages/flutter/test/services/raw_keyboard_test.dart b/packages/flutter/test/services/raw_keyboard_test.dart
index a19c5bb..b138267 100644
--- a/packages/flutter/test/services/raw_keyboard_test.dart
+++ b/packages/flutter/test/services/raw_keyboard_test.dart
@@ -17,6 +17,30 @@
void main() {
group('RawKeyboard', () {
+ testWidgets('The correct character is produced', (WidgetTester tester) async {
+ for (final String platform in <String>['linux', 'android', 'macos', 'fuchsia', 'windows']) {
+ String character = '';
+ void handleKey(RawKeyEvent event) {
+ expect(event.character, equals(character), reason: 'on $platform');
+ }
+ RawKeyboard.instance.addListener(handleKey);
+ character = 'a';
+ await simulateKeyDownEvent(LogicalKeyboardKey.keyA, platform: platform);
+ character = '`';
+ await simulateKeyDownEvent(LogicalKeyboardKey.backquote, platform: platform);
+ RawKeyboard.instance.removeListener(handleKey);
+ }
+ });
+ testWidgets('No character is produced for non-printables', (WidgetTester tester) async {
+ for (final String platform in <String>['linux', 'android', 'macos', 'fuchsia', 'windows']) {
+ void handleKey(RawKeyEvent event) {
+ expect(event.character, isNull, reason: 'on $platform');
+ }
+ RawKeyboard.instance.addListener(handleKey);
+ await simulateKeyDownEvent(LogicalKeyboardKey.shiftLeft, platform: platform);
+ RawKeyboard.instance.removeListener(handleKey);
+ }
+ });
testWidgets('keysPressed is maintained', (WidgetTester tester) async {
for (final String platform in <String>['linux', 'android', 'macos', 'fuchsia', 'windows']) {
RawKeyboard.instance.clearKeysPressed();
diff --git a/packages/flutter_test/lib/src/event_simulation.dart b/packages/flutter_test/lib/src/event_simulation.dart
index f7a65f2..2a624b8 100644
--- a/packages/flutter_test/lib/src/event_simulation.dart
+++ b/packages/flutter_test/lib/src/event_simulation.dart
@@ -174,7 +174,6 @@
final Map<String, dynamic> result = <String, dynamic>{
'type': isDown ? 'keydown' : 'keyup',
'keymap': platform,
- 'character': key.keyLabel,
};
switch (platform) {
@@ -182,6 +181,7 @@
result['keyCode'] = keyCode;
if (key.keyLabel.isNotEmpty) {
result['codePoint'] = key.keyLabel.codeUnitAt(0);
+ result['character'] = key.keyLabel;
}
result['scanCode'] = scanCode;
result['metaState'] = _getAndroidModifierFlags(key, isDown);
@@ -198,16 +198,19 @@
result['keyCode'] = keyCode;
result['scanCode'] = scanCode;
result['modifiers'] = _getGlfwModifierFlags(key, isDown);
+ result['unicodeScalarValues'] = key.keyLabel.isNotEmpty ? key.keyLabel.codeUnitAt(0) : 0;
break;
case 'macos':
result['keyCode'] = scanCode;
- result['characters'] = key.keyLabel;
- result['charactersIgnoringModifiers'] = key.keyLabel;
+ if (key.keyLabel.isNotEmpty) {
+ result['characters'] = key.keyLabel;
+ result['charactersIgnoringModifiers'] = key.keyLabel;
+ }
result['modifiers'] = _getMacOsModifierFlags(key, isDown);
break;
case 'web':
result['code'] = _getWebKeyCode(key);
- result['key'] = '';
+ result['key'] = key.keyLabel;
result['metaState'] = _getWebModifierFlags(key, isDown);
break;
case 'windows':