Finalize editing when hitting the enter key on a single line TextField (#23015)
Behavior remains the same for multiline TextFields. This behavior already happens on iOS and Android, but not desktop. #23014
diff --git a/packages/flutter/lib/src/widgets/editable_text.dart b/packages/flutter/lib/src/widgets/editable_text.dart
index 96785c0..c374cc4 100644
--- a/packages/flutter/lib/src/widgets/editable_text.dart
+++ b/packages/flutter/lib/src/widgets/editable_text.dart
@@ -511,44 +511,43 @@
void performAction(TextInputAction action) {
switch (action) {
case TextInputAction.newline:
- // Do nothing for a "newline" action: the newline is already inserted.
+ // If this is a multiline EditableText, do nothing for a "newline"
+ // action; The newline is already inserted. Otherwise, finalize
+ // editing.
+ if (widget.maxLines == 1)
+ _finalizeEditing(true);
break;
case TextInputAction.done:
case TextInputAction.go:
case TextInputAction.send:
case TextInputAction.search:
- // Take any actions necessary now that the user has completed editing.
- if (widget.onEditingComplete != null) {
- widget.onEditingComplete();
- } else {
- // Default behavior if the developer did not provide an
- // onEditingComplete callback: Finalize editing and remove focus.
- widget.controller.clearComposing();
- widget.focusNode.unfocus();
- }
-
- // Invoke optional callback with the user's submitted content.
- if (widget.onSubmitted != null)
- widget.onSubmitted(_value.text);
+ _finalizeEditing(true);
break;
default:
- if (widget.onEditingComplete != null) {
- widget.onEditingComplete();
- } else {
- // Default behavior if the developer did not provide an
- // onEditingComplete callback: Finalize editing, but don't give up
- // focus because this keyboard action does not imply the user is done
- // inputting information.
- widget.controller.clearComposing();
- }
-
- // Invoke optional callback with the user's submitted content.
- if (widget.onSubmitted != null)
- widget.onSubmitted(_value.text);
+ // Finalize editing, but don't give up focus because this keyboard
+ // action does not imply the user is done inputting information.
+ _finalizeEditing(false);
break;
}
}
+ void _finalizeEditing(bool shouldUnfocus) {
+ // Take any actions necessary now that the user has completed editing.
+ if (widget.onEditingComplete != null) {
+ widget.onEditingComplete();
+ } else {
+ // Default behavior if the developer did not provide an
+ // onEditingComplete callback: Finalize editing and remove focus.
+ widget.controller.clearComposing();
+ if (shouldUnfocus)
+ widget.focusNode.unfocus();
+ }
+
+ // Invoke optional callback with the user's submitted content.
+ if (widget.onSubmitted != null)
+ widget.onSubmitted(_value.text);
+ }
+
void _updateRemoteEditingValueIfNeeded() {
if (!_hasInputConnection)
return;
diff --git a/packages/flutter/test/widgets/editable_text_test.dart b/packages/flutter/test/widgets/editable_text_test.dart
index 8b502d8..f4a1b86 100644
--- a/packages/flutter/test/widgets/editable_text_test.dart
+++ b/packages/flutter/test/widgets/editable_text_test.dart
@@ -745,6 +745,94 @@
// and onSubmission callbacks.
});
+ testWidgets(
+ 'When "newline" action is called on a Editable text with maxLines == 1 callbacks are invoked: onEditingComplete > onSubmitted',
+ (WidgetTester tester) async {
+ final GlobalKey<EditableTextState> editableTextKey =
+ GlobalKey<EditableTextState>();
+ final FocusNode focusNode = FocusNode();
+
+ bool onEditingCompleteCalled = false;
+ bool onSubmittedCalled = false;
+
+ final Widget widget = MaterialApp(
+ home: EditableText(
+ key: editableTextKey,
+ controller: TextEditingController(),
+ focusNode: focusNode,
+ style: Typography(platform: TargetPlatform.android).black.subhead,
+ cursorColor: Colors.blue,
+ maxLines: 1,
+ onEditingComplete: () {
+ onEditingCompleteCalled = true;
+ assert(!onSubmittedCalled);
+ },
+ onSubmitted: (String value) {
+ onSubmittedCalled = true;
+ assert(onEditingCompleteCalled);
+ },
+ ),
+ );
+ await tester.pumpWidget(widget);
+
+ // Select EditableText to give it focus.
+ final Finder textFinder = find.byKey(editableTextKey);
+ await tester.tap(textFinder);
+ await tester.pump();
+
+ assert(focusNode.hasFocus);
+
+ // The execution path starting with receiveAction() will trigger the
+ // onEditingComplete and onSubmission callbacks.
+ await tester.testTextInput.receiveAction(TextInputAction.newline);
+ // The expectations we care about are up above in the onEditingComplete
+ // and onSubmission callbacks.
+ });
+
+testWidgets(
+ 'When "newline" action is called on a Editable text with maxLines != 1, onEditingComplete and onSubmitted callbacks are not invoked.',
+ (WidgetTester tester) async {
+ final GlobalKey<EditableTextState> editableTextKey =
+ GlobalKey<EditableTextState>();
+ final FocusNode focusNode = FocusNode();
+
+ bool onEditingCompleteCalled = false;
+ bool onSubmittedCalled = false;
+
+ final Widget widget = MaterialApp(
+ home: EditableText(
+ key: editableTextKey,
+ controller: TextEditingController(),
+ focusNode: focusNode,
+ style: Typography(platform: TargetPlatform.android).black.subhead,
+ cursorColor: Colors.blue,
+ maxLines: 3,
+ onEditingComplete: () {
+ onEditingCompleteCalled = true;
+ },
+ onSubmitted: (String value) {
+ onSubmittedCalled = true;
+ },
+ ),
+ );
+ await tester.pumpWidget(widget);
+
+ // Select EditableText to give it focus.
+ final Finder textFinder = find.byKey(editableTextKey);
+ await tester.tap(textFinder);
+ await tester.pump();
+
+ assert(focusNode.hasFocus);
+
+ // The execution path starting with receiveAction() will trigger the
+ // onEditingComplete and onSubmission callbacks.
+ await tester.testTextInput.receiveAction(TextInputAction.newline);
+
+ // These callbacks shouldn't have been triggered.
+ assert(!onSubmittedCalled);
+ assert(!onEditingCompleteCalled);
+ });
+
testWidgets('Changing controller updates EditableText',
(WidgetTester tester) async {
final GlobalKey<EditableTextState> editableTextKey =