-
Notifications
You must be signed in to change notification settings - Fork 29.7k
Description
Steps to reproduce
- Run the tests from the sample code
Expected results
Both tests should pass.
Actual results
The first test passes, but the second fails.
Code sample
Code sample
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';
void main() {
testWidgets('first test', (tester) async {
final myClient = _ExampleInputClient();
await _runTest(tester, myClient);
expect(myClient.performSelectorCalled, isTrue);
});
testWidgets('second test', (tester) async {
final myClient = _ExampleInputClient();
await _runTest(tester, myClient);
expect(myClient.performSelectorCalled, isTrue);
});
}
Future<void> _runTest(WidgetTester tester, TextInputClient client) async {
debugDefaultTargetPlatformOverride = TargetPlatform.macOS;
try {
final focusNode = FocusNode();
await tester.pumpWidget(
Directionality(
textDirection: TextDirection.ltr,
child: Scaffold(
body: Focus(
focusNode: focusNode,
autofocus: true,
child: Container(),
),
),
),
);
/// Attach to the IME.
final connection = TextInput.attach(client, const TextInputConfiguration());
addTearDown(() {
connection.close();
});
connection.show();
// Press the left arrow, which should trigger a "moveLeft:" selector call.
await tester.sendKeyEvent(LogicalKeyboardKey.arrowLeft, platform: 'macos');
await tester.pumpAndSettle();
// Intercept the messages after the text input connection is established.
// This will prevent the test text input from receiving the "clearClient" message,
// to clear the keyboard handler.
tester.binding.defaultBinaryMessenger.setMockMethodCallHandler(
SystemChannels.textInput,
(MethodCall methodCall) async {
return null;
},
);
} finally {
debugDefaultTargetPlatformOverride = null;
}
}
/// A [TextInputClient] that reports whether the `performSelector` method was called.
class _ExampleInputClient with TextInputClient {
bool get performSelectorCalled => _performSelectorCalled;
bool _performSelectorCalled = false;
@override
AutofillScope? get currentAutofillScope => null;
@override
TextEditingValue? get currentTextEditingValue => _currentTextEditingValue;
TextEditingValue _currentTextEditingValue = const TextEditingValue();
@override
void performSelector(String selectorName) {
super.performSelector(selectorName);
_performSelectorCalled = true;
}
@override
void connectionClosed() {}
@override
void updateEditingValue(TextEditingValue value) {
_currentTextEditingValue = value;
}
@override
void performAction(TextInputAction action) {}
@override
void performPrivateCommand(String action, Map<String, dynamic> data) {}
@override
void showAutocorrectionPromptRect(int start, int end) {}
@override
void updateFloatingCursor(RawFloatingCursorPoint point) {}
}Context
Developers might want to override the mock message handlers to perform some checks during tests. Overriding the handler for the text input channel can cause the text input to not work correctly in the subsequent tests.
The issue seems to be in TestTextInput.reset:
flutter/packages/flutter_test/lib/src/test_text_input.dart
Lines 123 to 129 in 0b28108
| void reset() { | |
| log.clear(); | |
| _client = null; | |
| setClientArgs = null; | |
| editingState = null; | |
| _isVisible = false; | |
| } |
This method does not clear the _keyHandler. When the text input is reattached, it does not override the key handler. As the result, we keep a handler with the outdated client id, which causes the text input client to drop messages:
flutter/packages/flutter/lib/src/services/text_input.dart
Lines 2073 to 2090 in 0b28108
| final int client = args[0] as int; | |
| if (client != _currentConnection!._id) { | |
| // If the client IDs don't match, the incoming message was for a different | |
| // client. | |
| bool debugAllowAnyway = false; | |
| assert(() { | |
| // In debug builds we allow "-1" as a magical client ID that ignores | |
| // this verification step so that tests can always get through, even | |
| // when they are not mocking the engine side of text input. | |
| if (client == -1) { | |
| debugAllowAnyway = true; | |
| } | |
| return true; | |
| }()); | |
| if (!debugAllowAnyway) { | |
| return; | |
| } | |
| } |
Logs
Flutter Doctor output
Doctor output
$ flutter doctor -v
[✓] Flutter (Channel stable, 3.32.4, on macOS 15.5 24F74 darwin-arm64, locale en-BR) [467ms]
• Flutter version 3.32.4 on channel stable at /Users/angelosilvestre/dev/flutter
• Upstream repository https://github.com/flutter/flutter.git
• Framework revision 6fba2447e9 (3 weeks ago), 2025-06-12 19:03:56 -0700
• Engine revision 8cd19e509d
• Dart version 3.8.1
• DevTools version 2.45.1
[✓] Android toolchain - develop for Android devices (Android SDK version 34.0.0) [4.0s]
• Android SDK at /Users/angelosilvestre/Library/Android/sdk
• Platform android-35, build-tools 34.0.0
• Java binary at: /Applications/Android Studio.app/Contents/jbr/Contents/Home/bin/java
This is the JDK bundled with the latest Android Studio installation on this machine.
To manually set the JDK path, use: `flutter config --jdk-dir="path/to/jdk"`.
• Java version OpenJDK Runtime Environment (build 17.0.6+0-17.0.6b829.9-10027231)
• All Android licenses accepted.
[✓] Xcode - develop for iOS and macOS (Xcode 16.4) [961ms]
• Xcode at /Applications/Xcode.app/Contents/Developer
• Build 16F6
• CocoaPods version 1.16.2
[✓] Chrome - develop for the web [10ms]
• Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome
[✓] Android Studio (version 2022.3) [10ms]
• 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 17.0.6+0-17.0.6b829.9-10027231)
[✓] VS Code (version 1.100.2) [8ms]
• VS Code at /Applications/Visual Studio Code.app/Contents
• Flutter extension version 3.112.0
[✓] Connected device (2 available) [6.2s]
• macOS (desktop) • macos • darwin-arm64 • macOS 15.5 24F74 darwin-arm64
• Chrome (web) • chrome • web-javascript • Google Chrome 137.0.7151.120
[✓] Network resources [984ms]
• All expected network resources are available.
• No issues found!