Repository navigation
Repeated hardware keyboard events not received on iOS #99252
Description
KeyRepeatEvent notifications are not received by a Flutter app when using a hardware keyboard connected to an iOS device or simulator. It works on Android.
When testing on physical devices I used an MX Keys keyboard connected via Bluetooth. For the iOS simulator test, I used the built-in keyboard on the host machine.
The code sample below uses a Focus, but I also tested FocusScope, CallbackShortcuts, KeyboardListener, and RawKeyboardListener. The behavior is the same.
Steps to Reproduce
- Execute
flutter runon the code sample - Hold down any key on a connected hardware keyboard
- Observe the text on the screen
Expected results:
All devices show the text "KeyDownEvent" and then it quickly changes to "KeyRepeatEvent"
Actual results:
The iOS devices are stuck on "KeyDownEvent" and don't show "KeyRepeatEvent"
| iPhone 12 Pro | iPhone simulator | Samsung Galaxy A12: |
|---|---|---|
|
|
|
Code sample
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);
@override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
String _text = "Hold down a key";
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Scaffold(
body: Center(
child: Focus(
autofocus: true,
onKeyEvent: (_, event) {
setState(() {
_text = '${event.runtimeType}';
});
return KeyEventResult.handled;
},
child: Text(_text, style: const TextStyle(fontSize: 40),
)
),
),
),
);
}
}Logs
[ +2 ms] Flutter run key commands.
[ +1 ms] r Hot reload. 🔥🔥🔥
[ ] R Hot restart.
[ ] h List all available interactive commands.
[ ] d Detach (terminate "flutter run" but leave application running).
[ ] c Clear the screen
[ ] q Quit (terminate the application on the device).
[ ] 💪 Running with sound null safety 💪
[ ] An Observatory debugger and profiler on iPhone is available at: http://127.0.0.1:60927/Y2196qBaw50=/
[ +255 ms] The Flutter DevTools debugger and profiler on iPhone is available at: http://127.0.0.1:9100?uri=http://127.0.0.1:60927/Y2196qBaw50=/
% flutter analyze
Analyzing hardware_keyboard_test...
No issues found! (ran in 1.5s)
Flutter doctor shows an issue with IntelliJ but I don't think it's related. I'm launching using the command line.
[✓] Flutter (Channel stable, 2.10.2, on macOS 12.2 21D49 darwin-x64, locale en-US)
• Flutter version 2.10.2 at /Users/jackson/flutter
• Upstream repository https://github.com/flutter/flutter.git
• Framework revision 097d3313d8 (9 days ago), 2022-02-18 19:33:08 -0600
• Engine revision a83ed0e5e3
• Dart version 2.16.1
• DevTools version 2.9.2
[✓] Android toolchain - develop for Android devices (Android SDK version 32.0.0)
• Android SDK at /Users/jackson/Library/Android/sdk
• Platform android-32, build-tools 32.0.0
• ANDROID_SDK_ROOT = /home/jackson/Library/Android/sdk
• Java binary at: /Applications/Android Studio.app/Contents/jre/Contents/Home/bin/java
• Java version OpenJDK Runtime Environment (build 11.0.10+0-b96-7281165)
• All Android licenses accepted.
[✓] Xcode - develop for iOS and macOS (Xcode 13.2.1)
• Xcode at /Applications/Xcode.app/Contents/Developer
• CocoaPods version 1.11.2
[✓] Chrome - develop for the web
• Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome
[✓] Android Studio (version 2020.3)
• Android Studio at /Applications/Android Studio.app/Contents
• Flutter plugin can be installed from:
🔨 https://plugins.jetbrains.com/plugin/9212-flutter
• Dart plugin can be installed from:
🔨 https://plugins.jetbrains.com/plugin/6351-dart
• Java version OpenJDK Runtime Environment (build 11.0.10+0-b96-7281165)
[☠] IntelliJ IDEA Community Edition (the doctor check crashed)
✗ Due to an error, the doctor check did not complete. If the error message below is not helpful, please let us know about this issue at
https://github.com/flutter/flutter/issues.
✗ FormatException: Unexpected extension byte (at offset 5)
• #0 _Utf8Decoder.convertSingle (dart:convert-patch/convert_patch.dart:1789:7)
#1 Utf8Decoder.convert (dart:convert/utf.dart:351:42)
#2 InputStream.readString (package:archive/src/util/input_stream.dart:207:30)
#3 new ZipDirectory.read (package:archive/src/zip/zip_directory.dart:40:30)
#4 ZipDecoder.decodeBuffer (package:archive/src/zip_decoder.dart:19:30)
#5 ZipDecoder.decodeBytes (package:archive/src/zip_decoder.dart:14:12)
#6 IntelliJPlugins._findPluginXml (package:flutter_tools/src/intellij/intellij.dart:130:44)
#7 IntelliJPlugins._readPackageVersion (package:flutter_tools/src/intellij/intellij.dart:141:40)
#8 IntelliJPlugins.validatePackage (package:flutter_tools/src/intellij/intellij.dart:63:35)
#9 IntelliJValidator.validate (package:flutter_tools/src/intellij/intellij_validator.dart:103:15)
#10 asyncGuard.<anonymous closure> (package:flutter_tools/src/base/async_guard.dart:111:32)
#11 asyncGuard.<anonymous closure> (package:flutter_tools/src/base/async_guard.dart:109:18)
#12 _rootRun (dart:async/zone.dart:1426:13)
#13 _CustomZone.run (dart:async/zone.dart:1328:19)
#14 _runZoned (dart:async/zone.dart:1861:10)
#15 runZonedGuarded (dart:async/zone.dart:1849:12)
#16 runZoned (dart:async/zone.dart:1780:12)
#17 asyncGuard (package:flutter_tools/src/base/async_guard.dart:109:3)
#18 Doctor.startValidatorTasks (package:flutter_tools/src/doctor.dart:205:9)
#19 Doctor.diagnose (package:flutter_tools/src/doctor.dart:309:47)
#20 DoctorCommand.runCommand (package:flutter_tools/src/commands/doctor.dart:50:48)
#21 FlutterCommand.verifyThenRunCommand (package:flutter_tools/src/runner/flutter_command.dart:1320:12)
<asynchronous suspension>
#22 FlutterCommand.run.<anonymous closure> (package:flutter_tools/src/runner/flutter_command.dart:1161:27)
<asynchronous suspension>
#23 AppContext.run.<anonymous closure> (package:flutter_tools/src/base/context.dart:150:19)
<asynchronous suspension>
#24 CommandRunner.runCommand (package:args/command_runner.dart:209:13)
<asynchronous suspension>
#25 FlutterCommandRunner.runCommand.<anonymous closure> (package:flutter_tools/src/runner/flutter_command_runner.dart:281:9)
<asynchronous suspension>
#26 AppContext.run.<anonymous closure> (package:flutter_tools/src/base/context.dart:150:19)
<asynchronous suspension>
#27 FlutterCommandRunner.runCommand (package:flutter_tools/src/runner/flutter_command_runner.dart:229:5)
<asynchronous suspension>
#28 run.<anonymous closure>.<anonymous closure> (package:flutter_tools/runner.dart:62:9)
<asynchronous suspension>
#29 AppContext.run.<anonymous closure> (package:flutter_tools/src/base/context.dart:150:19)
<asynchronous suspension>
#30 main (package:flutter_tools/executable.dart:94:3)
<asynchronous suspension>
[✓] Connected device (5 available)
• SM A125U (mobile) • R58R85BBCAV • android-arm64 • Android 11 (API 30)
• iPhone (mobile) • 00008101-001155042188801E • ios • iOS 15.1 19B74
• iPhone 12 Pro (mobile) • 132369FE-696E-42C9-B0F4-EE1A24E377FC • ios • com.apple.CoreSimulator.SimRuntime.iOS-15-2 (simulator)
• macOS (desktop) • macos • darwin-x64 • macOS 12.2 21D49 darwin-x64
• Chrome (web) • chrome • web-javascript • Google Chrome 98.0.4758.109
[✓] HTTP Host Availability
• All required HTTP hosts are available
! Doctor found issues in 1 category.



Activity
jmagman commentedon Feb 28, 2022
@dkwingsmt or @gspencergoog I know you're not working on iOS but you may have the most context for key event issues like this.
gspencergoog commentedon Feb 28, 2022
As far as I know, iOS doesn't provide key repeat event information through their API, so we can't generate those events.
gspencergoog commentedon Feb 28, 2022
At least that was true at the time the iOS key event code was implemented.
chinmaygarde commentedon Feb 28, 2022
@gspencergoog So, is this working as intended? Also, hey Collin 👋
gspencergoog commentedon Feb 28, 2022
Oh! I didn't even notice this was from Collin! Hey Collin!
gspencergoog commentedon Feb 28, 2022
Well, we intend to provide key repeat events, but Apple doesn't. :-)
collinjackson commentedon Feb 28, 2022
It sounds like the issue is not possible to fix in Flutter and can be closed. Perhaps the documentation for
KeyRepeatEventcould mention that repeat events are provided if supported by the underlying platform. Creative use ofTextFormFieldor flutter_native_text_input may be better options than making guesses about the user's repeat settings.dkwingsmt commentedon Jan 10, 2023
Is it possible to simulate this behavior, such as listening to the iOS text API and synthesize a repeat event if the same text keeps coming?
github-actions commentedon Mar 4, 2023
This thread has been automatically locked since there has not been any recent activity after it was closed. If you are still experiencing a similar issue, please open a new bug, including the output of
flutter doctor -vand a minimal reproduction of the issue.