From 3ca6445c99ac49fdd2cd0169084afaae17809a51 Mon Sep 17 00:00:00 2001 From: matthew-carroll Date: Thu, 19 Jul 2018 13:22:45 -0700 Subject: [PATCH] Added textInputAction parameter to TextFormField that forwards to TextField, added onEditingComplete parameter to TextField that forwards to EditableText, added onEditingComplete parameter to TextFormField that forwards to TextField. (#19397) (#19427) --- .../flutter/lib/src/material/text_field.dart | 20 +++++++++ .../lib/src/material/text_form_field.dart | 5 +++ .../test/material/text_field_test.dart | 20 +++++++++ .../test/material/text_form_field_test.dart | 42 +++++++++++++++++++ 4 files changed, 87 insertions(+) diff --git a/packages/flutter/lib/src/material/text_field.dart b/packages/flutter/lib/src/material/text_field.dart index f82f011245e..ea506ee02da 100644 --- a/packages/flutter/lib/src/material/text_field.dart +++ b/packages/flutter/lib/src/material/text_field.dart @@ -112,6 +112,7 @@ class TextField extends StatefulWidget { this.maxLength, this.maxLengthEnforced = true, this.onChanged, + this.onEditingComplete, this.onSubmitted, this.inputFormatters, this.enabled, @@ -263,6 +264,24 @@ class TextField extends StatefulWidget { /// Called when the text being edited changes. final ValueChanged onChanged; + /// Called when the user submits editable content (e.g., user presses the "done" + /// button on the keyboard). + /// + /// The default implementation of [onEditingComplete] executes 2 different + /// behaviors based on the situation: + /// + /// - When a completion action is pressed, such as "done", "go", "send", or + /// "search", the user's content is submitted to the [controller] and then + /// focus is given up. + /// + /// - When a non-completion action is pressed, such as "next" or "previous", + /// the user's content is submitted to the [controller], but focus is not + /// given up because developers may want to immediately move focus to + /// another input widget within [onSubmitted]. + /// + /// Providing [onEditingComplete] prevents the aforementioned default behavior. + final VoidCallback onEditingComplete; + /// Called when the user indicates that they are done editing the text in the /// field. final ValueChanged onSubmitted; @@ -502,6 +521,7 @@ class _TextFieldState extends State with AutomaticKeepAliveClientMixi ? cupertinoTextSelectionControls : materialTextSelectionControls, onChanged: widget.onChanged, + onEditingComplete: widget.onEditingComplete, onSubmitted: widget.onSubmitted, onSelectionChanged: _handleSelectionChanged, inputFormatters: formatters, diff --git a/packages/flutter/lib/src/material/text_form_field.dart b/packages/flutter/lib/src/material/text_form_field.dart index d9afab10d2f..8b5774c00a8 100644 --- a/packages/flutter/lib/src/material/text_form_field.dart +++ b/packages/flutter/lib/src/material/text_form_field.dart @@ -55,6 +55,7 @@ class TextFormField extends FormField { FocusNode focusNode, InputDecoration decoration = const InputDecoration(), TextInputType keyboardType = TextInputType.text, + TextInputAction textInputAction = TextInputAction.done, TextStyle style, TextAlign textAlign = TextAlign.start, bool autofocus = false, @@ -64,6 +65,7 @@ class TextFormField extends FormField { bool maxLengthEnforced = true, int maxLines = 1, int maxLength, + VoidCallback onEditingComplete, ValueChanged onFieldSubmitted, FormFieldSetter onSaved, FormFieldValidator validator, @@ -72,6 +74,7 @@ class TextFormField extends FormField { Brightness keyboardAppearance, }) : assert(initialValue == null || controller == null), assert(keyboardType != null), + assert(textInputAction != null), assert(textAlign != null), assert(autofocus != null), assert(obscureText != null), @@ -95,6 +98,7 @@ class TextFormField extends FormField { focusNode: focusNode, decoration: effectiveDecoration.copyWith(errorText: field.errorText), keyboardType: keyboardType, + textInputAction: textInputAction, style: style, textAlign: textAlign, autofocus: autofocus, @@ -104,6 +108,7 @@ class TextFormField extends FormField { maxLines: maxLines, maxLength: maxLength, onChanged: field.didChange, + onEditingComplete: onEditingComplete, onSubmitted: onFieldSubmitted, inputFormatters: inputFormatters, enabled: enabled, diff --git a/packages/flutter/test/material/text_field_test.dart b/packages/flutter/test/material/text_field_test.dart index 6702312f7d2..ef8bd68b1dd 100644 --- a/packages/flutter/test/material/text_field_test.dart +++ b/packages/flutter/test/material/text_field_test.dart @@ -168,6 +168,26 @@ void main() { debugResetSemanticsIdCounter(); }); + testWidgets('TextField passes onEditingComplete to EditableText', (WidgetTester tester) async { + final VoidCallback onEditingComplete = () {}; + + await tester.pumpWidget( + new MaterialApp( + home: new Material( + child: new TextField( + onEditingComplete: onEditingComplete, + ), + ), + ), + ); + + final Finder editableTextFinder = find.byType(EditableText); + expect(editableTextFinder, findsOneWidget); + + final EditableText editableTextWidget = tester.widget(editableTextFinder); + expect(editableTextWidget.onEditingComplete, onEditingComplete); + }); + testWidgets('TextField has consistent size', (WidgetTester tester) async { final Key textFieldKey = new UniqueKey(); String textFieldValue; diff --git a/packages/flutter/test/material/text_form_field_test.dart b/packages/flutter/test/material/text_form_field_test.dart index 43208bdfec1..c3c3b5cbed2 100644 --- a/packages/flutter/test/material/text_form_field_test.dart +++ b/packages/flutter/test/material/text_form_field_test.dart @@ -29,6 +29,48 @@ void main() { expect(textFieldWidget.textAlign, alignment); }); + testWidgets('Passes textInputAction to underlying TextField', (WidgetTester tester) async { + await tester.pumpWidget( + new MaterialApp( + home: new Material( + child: new Center( + child: new TextFormField( + textInputAction: TextInputAction.next, + ), + ), + ), + ), + ); + + final Finder textFieldFinder = find.byType(TextField); + expect(textFieldFinder, findsOneWidget); + + final TextField textFieldWidget = tester.widget(textFieldFinder); + expect(textFieldWidget.textInputAction, TextInputAction.next); + }); + + testWidgets('Passes onEditingComplete to underlying TextField', (WidgetTester tester) async { + final VoidCallback onEditingComplete = () {}; + + await tester.pumpWidget( + new MaterialApp( + home: new Material( + child: new Center( + child: new TextFormField( + onEditingComplete: onEditingComplete, + ), + ), + ), + ), + ); + + final Finder textFieldFinder = find.byType(TextField); + expect(textFieldFinder, findsOneWidget); + + final TextField textFieldWidget = tester.widget(textFieldFinder); + expect(textFieldWidget.onEditingComplete, onEditingComplete); + }); + testWidgets('onFieldSubmit callbacks are called', (WidgetTester tester) async { bool _called = false;