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
This commit is contained in:
parent
ffa8a1ebe8
commit
d556d2117d
@ -511,44 +511,43 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
|
|||||||
void performAction(TextInputAction action) {
|
void performAction(TextInputAction action) {
|
||||||
switch (action) {
|
switch (action) {
|
||||||
case TextInputAction.newline:
|
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;
|
break;
|
||||||
case TextInputAction.done:
|
case TextInputAction.done:
|
||||||
case TextInputAction.go:
|
case TextInputAction.go:
|
||||||
case TextInputAction.send:
|
case TextInputAction.send:
|
||||||
case TextInputAction.search:
|
case TextInputAction.search:
|
||||||
// Take any actions necessary now that the user has completed editing.
|
_finalizeEditing(true);
|
||||||
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);
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
if (widget.onEditingComplete != null) {
|
// Finalize editing, but don't give up focus because this keyboard
|
||||||
widget.onEditingComplete();
|
// action does not imply the user is done inputting information.
|
||||||
} else {
|
_finalizeEditing(false);
|
||||||
// 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);
|
|
||||||
break;
|
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() {
|
void _updateRemoteEditingValueIfNeeded() {
|
||||||
if (!_hasInputConnection)
|
if (!_hasInputConnection)
|
||||||
return;
|
return;
|
||||||
|
@ -745,6 +745,94 @@ void main() {
|
|||||||
// and onSubmission callbacks.
|
// 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',
|
testWidgets('Changing controller updates EditableText',
|
||||||
(WidgetTester tester) async {
|
(WidgetTester tester) async {
|
||||||
final GlobalKey<EditableTextState> editableTextKey =
|
final GlobalKey<EditableTextState> editableTextKey =
|
||||||
|
Loading…
x
Reference in New Issue
Block a user