From 8538b4f546c9975f70de1e473eb4d8db8e52475e Mon Sep 17 00:00:00 2001 From: xubaolin Date: Tue, 13 Oct 2020 00:37:03 +0800 Subject: [PATCH] Fix TextField bug when the formatter repeatedly format (#67892) --- .../lib/src/widgets/editable_text.dart | 11 ++- .../test/widgets/editable_text_test.dart | 78 +++++++++++++++++++ 2 files changed, 87 insertions(+), 2 deletions(-) diff --git a/packages/flutter/lib/src/widgets/editable_text.dart b/packages/flutter/lib/src/widgets/editable_text.dart index 5bb9612f152..97a8dd13006 100644 --- a/packages/flutter/lib/src/widgets/editable_text.dart +++ b/packages/flutter/lib/src/widgets/editable_text.dart @@ -2212,8 +2212,15 @@ class EditableTextState extends State with AutomaticKeepAliveClien _lastFormattedValue = value; } - // Setting _value here ensures the selection and composing region info is passed. - _value = value; + if (value == _value) { + // If the value was modified by the formatter, the remote should be notified to keep in sync, + // if not modified, it will short-circuit. + _updateRemoteEditingValueIfNeeded(); + } else { + // Setting _value here ensures the selection and composing region info is passed. + _value = value; + } + // Use the last formatted value when an identical repeat pass is detected. if (isRepeat && textChanged && _lastFormattedValue != null) { _value = _lastFormattedValue!; diff --git a/packages/flutter/test/widgets/editable_text_test.dart b/packages/flutter/test/widgets/editable_text_test.dart index 2872d7a39b4..01f8942729d 100644 --- a/packages/flutter/test/widgets/editable_text_test.dart +++ b/packages/flutter/test/widgets/editable_text_test.dart @@ -4998,6 +4998,84 @@ void main() { ); }); + testWidgets('Send text input state to engine when the input formatter rejects user input', (WidgetTester tester) async { + // Regression test for https://github.com/flutter/flutter/issues/67828 + final List log = []; + SystemChannels.textInput.setMockMethodCallHandler((MethodCall methodCall) async { + log.add(methodCall); + }); + final TextInputFormatter formatter = TextInputFormatter.withFunction((TextEditingValue oldValue, TextEditingValue newValue) { + return const TextEditingValue(text: 'Flutter is the best!'); + }); + final TextEditingController controller = TextEditingController(); + + final FocusNode focusNode = FocusNode(debugLabel: 'EditableText Focus Node'); + Widget builder() { + return StatefulBuilder( + builder: (BuildContext context, StateSetter setter) { + return MaterialApp( + home: MediaQuery( + data: const MediaQueryData(devicePixelRatio: 1.0), + child: Directionality( + textDirection: TextDirection.ltr, + child: Center( + child: Material( + child: EditableText( + controller: controller, + focusNode: focusNode, + style: textStyle, + cursorColor: Colors.red, + backgroundCursorColor: Colors.red, + keyboardType: TextInputType.multiline, + inputFormatters: [ + formatter, + ], + onChanged: (String value) { }, + ), + ), + ), + ), + ), + ); + }, + ); + } + + await tester.pumpWidget(builder()); + await tester.tap(find.byType(EditableText)); + await tester.showKeyboard(find.byType(EditableText)); + await tester.pump(); + + log.clear(); + + final EditableTextState state = tester.firstState(find.byType(EditableText)); + + // setEditingState is called when remote value modified by the formatter. + state.updateEditingValue(const TextEditingValue( + text: 'I will be modified by the formatter.', + )); + expect(log.length, 1); + expect(log, contains(matchesMethodCall( + 'TextInput.setEditingState', + args: allOf( + containsPair('text', 'Flutter is the best!'), + ), + ))); + + log.clear(); + + state.updateEditingValue(const TextEditingValue( + text: 'I will be modified by the formatter.', + )); + expect(log.length, 1); + expect(log, contains(matchesMethodCall( + 'TextInput.setEditingState', + args: allOf( + containsPair('text', 'Flutter is the best!'), + ), + ))); + }); + testWidgets('autofocus:true on first frame does not throw', (WidgetTester tester) async { final TextEditingController controller = TextEditingController(text: testText); controller.selection = const TextSelection(