Ignore key events on edit control on web platform (#52656) (#52661)
diff --git a/packages/flutter/lib/src/rendering/editable.dart b/packages/flutter/lib/src/rendering/editable.dart
index fa62b49..11e0f98 100644
--- a/packages/flutter/lib/src/rendering/editable.dart
+++ b/packages/flutter/lib/src/rendering/editable.dart
@@ -473,6 +473,11 @@
// string using Unicode scalar values, rather than using the number of
// extended grapheme clusters (a.k.a. "characters" in the end user's mind).
void _handleKeyEvent(RawKeyEvent keyEvent) {
+ if(kIsWeb) {
+ // On web platform, we should ignore the key because it's processed already.
+ return;
+ }
+
if (keyEvent is! RawKeyDownEvent || onSelectionChanged == null)
return;
final Set<LogicalKeyboardKey> keysPressed = LogicalKeyboardKey.collapseSynonyms(RawKeyboard.instance.keysPressed);
@@ -492,7 +497,7 @@
final bool isLineModifierPressed = isMacOS ? keyEvent.isMetaPressed : keyEvent.isAltPressed;
final bool isShortcutModifierPressed = isMacOS ? keyEvent.isMetaPressed : keyEvent.isControlPressed;
if (_movementKeys.contains(key)) {
- _handleMovement(key, wordModifier: isWordModifierPressed, lineModifier: isLineModifierPressed, shift: keyEvent.isShiftPressed);
+ _handleMovement(key, wordModifier: isWordModifierPressed, lineModifier: isLineModifierPressed, shift: keyEvent.isShiftPressed);
} else if (isShortcutModifierPressed && _shortcutKeys.contains(key)) {
// _handleShortcuts depends on being started in the same stack invocation
// as the _handleKeyEvent method
diff --git a/packages/flutter/test/rendering/editable_test.dart b/packages/flutter/test/rendering/editable_test.dart
index c25d402..525083f 100644
--- a/packages/flutter/test/rendering/editable_test.dart
+++ b/packages/flutter/test/rendering/editable_test.dart
@@ -368,6 +368,58 @@
expect(editable, paintsExactlyCountTimes(#drawRect, 1));
}, skip: isBrowser);
+ test('ignore key event from web platform', () async {
+ final TextSelectionDelegate delegate = FakeEditableTextState();
+ final ViewportOffset viewportOffset = ViewportOffset.zero();
+ TextSelection currentSelection;
+ final RenderEditable editable = RenderEditable(
+ backgroundCursorColor: Colors.grey,
+ selectionColor: Colors.black,
+ textDirection: TextDirection.ltr,
+ cursorColor: Colors.red,
+ offset: viewportOffset,
+ // This makes the scroll axis vertical.
+ maxLines: 2,
+ textSelectionDelegate: delegate,
+ onSelectionChanged: (TextSelection selection, RenderEditable renderObject, SelectionChangedCause cause) {
+ currentSelection = selection;
+ },
+ startHandleLayerLink: LayerLink(),
+ endHandleLayerLink: LayerLink(),
+ text: const TextSpan(
+ text: 'test\ntest',
+ style: TextStyle(
+ height: 1.0, fontSize: 10.0, fontFamily: 'Ahem',
+ ),
+ ),
+ selection: const TextSelection.collapsed(
+ offset: 4,
+ ),
+ );
+
+ layout(editable);
+ editable.hasFocus = true;
+
+ expect(
+ editable,
+ paints..paragraph(offset: Offset.zero),
+ );
+
+ editable.selectPositionAt(from: const Offset(0, 0), cause: SelectionChangedCause.tap);
+ editable.selection = const TextSelection.collapsed(offset: 0);
+ pumpFrame();
+
+ if(kIsWeb) {
+ await simulateKeyDownEvent(LogicalKeyboardKey.arrowRight, platform: 'web');
+ expect(currentSelection.isCollapsed, true);
+ expect(currentSelection.baseOffset, 0);
+ } else {
+ await simulateKeyDownEvent(LogicalKeyboardKey.arrowRight, platform: 'android');
+ expect(currentSelection.isCollapsed, true);
+ expect(currentSelection.baseOffset, 1);
+ }
+ });
+
test('selects correct place with offsets', () {
final TextSelectionDelegate delegate = FakeEditableTextState();
final ViewportOffset viewportOffset = ViewportOffset.zero();
diff --git a/packages/flutter_test/lib/src/event_simulation.dart b/packages/flutter_test/lib/src/event_simulation.dart
index 388d722..2a96d31 100644
--- a/packages/flutter_test/lib/src/event_simulation.dart
+++ b/packages/flutter_test/lib/src/event_simulation.dart
@@ -39,6 +39,7 @@
case 'fuchsia':
case 'macos':
case 'linux':
+ case 'web':
return true;
}
return false;
@@ -61,6 +62,9 @@
case 'linux':
map = kLinuxToPhysicalKey;
break;
+ case 'web':
+ // web doesn't have int type code
+ return null;
}
for (final int code in map.keys) {
if (key.usbHidUsage == map[code].usbHidUsage) {
@@ -85,6 +89,9 @@
case 'macos':
// macOS doesn't do key codes, just scan codes.
return null;
+ case 'web':
+ // web doesn't have int type code
+ return null;
case 'linux':
map = kGlfwToLogicalKey;
break;
@@ -97,10 +104,18 @@
}
return keyCode;
}
+ static String _getWebKeyCode(LogicalKeyboardKey key) {
+ for (final String code in kWebToLogicalKey.keys) {
+ if (key.keyId == kWebToLogicalKey[code].keyId) {
+ return code;
+ }
+ }
+ return null;
+ }
static PhysicalKeyboardKey _findPhysicalKey(LogicalKeyboardKey key, String platform) {
assert(_osIsSupported(platform), 'Platform $platform not supported for key simulation');
- Map<int, PhysicalKeyboardKey> map;
+ Map<dynamic, PhysicalKeyboardKey> map;
switch (platform) {
case 'android':
map = kAndroidToPhysicalKey;
@@ -114,6 +129,9 @@
case 'linux':
map = kLinuxToPhysicalKey;
break;
+ case 'web':
+ map = kWebToPhysicalKey;
+ break;
}
for (final PhysicalKeyboardKey physicalKey in map.values) {
if (key.debugName == physicalKey.debugName) {
@@ -138,10 +156,10 @@
physicalKey ??= _findPhysicalKey(key, platform);
assert(key.debugName != null);
- final int keyCode = platform == 'macos' ? -1 : _getKeyCode(key, platform);
- assert(platform == 'macos' || keyCode != null, 'Key $key not found in $platform keyCode map');
- final int scanCode = _getScanCode(physicalKey, platform);
- assert(scanCode != null, 'Physical key for $key not found in $platform scanCode map');
+ final int keyCode = platform == 'macos' || platform == 'web' ? -1 : _getKeyCode(key, platform);
+ assert(platform == 'macos' || platform == 'web' || keyCode != null, 'Key $key not found in $platform keyCode map');
+ final int scanCode = platform == 'web' ? -1 : _getScanCode(physicalKey, platform);
+ assert(platform == 'web' || scanCode != null, 'Physical key for $key not found in $platform scanCode map');
final Map<String, dynamic> result = <String, dynamic>{
'type': isDown ? 'keydown' : 'keyup',
@@ -173,6 +191,10 @@
result['charactersIgnoringModifiers'] = key.keyLabel;
result['modifiers'] = _getMacOsModifierFlags(key, isDown);
break;
+ case 'web':
+ result['code'] = _getWebKeyCode(key);
+ result['key'] = '';
+ result['metaState'] = _getWebModifierFlags(key, isDown);
}
return result;
}
@@ -288,6 +310,50 @@
return result;
}
+ static int _getWebModifierFlags(LogicalKeyboardKey newKey, bool isDown) {
+ int result = 0;
+ final Set<LogicalKeyboardKey> pressed = RawKeyboard.instance.keysPressed;
+ if (isDown) {
+ pressed.add(newKey);
+ } else {
+ pressed.remove(newKey);
+ }
+ if (pressed.contains(LogicalKeyboardKey.shiftLeft)) {
+ result |= RawKeyEventDataWeb.modifierShift;
+ }
+ if (pressed.contains(LogicalKeyboardKey.shiftRight)) {
+ result |= RawKeyEventDataWeb.modifierShift;
+ }
+ if (pressed.contains(LogicalKeyboardKey.metaLeft)) {
+ result |= RawKeyEventDataWeb.modifierMeta;
+ }
+ if (pressed.contains(LogicalKeyboardKey.metaRight)) {
+ result |= RawKeyEventDataWeb.modifierMeta;
+ }
+ if (pressed.contains(LogicalKeyboardKey.controlLeft)) {
+ result |= RawKeyEventDataWeb.modifierControl;
+ }
+ if (pressed.contains(LogicalKeyboardKey.controlRight)) {
+ result |= RawKeyEventDataWeb.modifierControl;
+ }
+ if (pressed.contains(LogicalKeyboardKey.altLeft)) {
+ result |= RawKeyEventDataWeb.modifierAlt;
+ }
+ if (pressed.contains(LogicalKeyboardKey.altRight)) {
+ result |= RawKeyEventDataWeb.modifierAlt;
+ }
+ if (pressed.contains(LogicalKeyboardKey.capsLock)) {
+ result |= RawKeyEventDataWeb.modifierCapsLock;
+ }
+ if (pressed.contains(LogicalKeyboardKey.numLock)) {
+ result |= RawKeyEventDataWeb.modifierNumLock;
+ }
+ if (pressed.contains(LogicalKeyboardKey.scrollLock)) {
+ result |= RawKeyEventDataWeb.modifierScrollLock;
+ }
+ return result;
+ }
+
static int _getMacOsModifierFlags(LogicalKeyboardKey newKey, bool isDown) {
int result = 0;
final Set<LogicalKeyboardKey> pressed = RawKeyboard.instance.keysPressed;