From 6280b391643dcc95a20c2f0716bfa0876599a3d4 Mon Sep 17 00:00:00 2001 From: Gary Qian Date: Tue, 31 Mar 2020 13:16:02 -0700 Subject: [PATCH] Un-gate value setting in formatter repeat check logic (#53613) --- .../lib/src/widgets/editable_text.dart | 10 ++--- .../test/widgets/editable_text_test.dart | 43 +++++++++++++++++++ 2 files changed, 48 insertions(+), 5 deletions(-) diff --git a/packages/flutter/lib/src/widgets/editable_text.dart b/packages/flutter/lib/src/widgets/editable_text.dart index e2c271ba0a..fcfaf63913 100644 --- a/packages/flutter/lib/src/widgets/editable_text.dart +++ b/packages/flutter/lib/src/widgets/editable_text.dart @@ -1676,13 +1676,13 @@ class EditableTextState extends State with AutomaticKeepAliveClien value = _whitespaceFormatter.formatEditUpdate(_value, value); _lastFormattedValue = value; } - // If the text, selection, or composing region has changed, we should update the - // locally stored TextEditingValue to the new one. - if (!isRepeatText || !isRepeatSelection || !isRepeatComposing) { - _value = value; - } else if (textChanged && _lastFormattedValue != null) { + + _value = value; + // Use the last formatted value when an identical repeat pass is detected. + if (isRepeatText && isRepeatSelection && isRepeatComposing && textChanged && _lastFormattedValue != null) { _value = _lastFormattedValue; } + // Always attempt to send the value. If the value has changed, then it will send, // otherwise, it will short-circuit. _updateRemoteEditingValueIfNeeded(); diff --git a/packages/flutter/test/widgets/editable_text_test.dart b/packages/flutter/test/widgets/editable_text_test.dart index 916e9947b0..1ef5b6f831 100644 --- a/packages/flutter/test/widgets/editable_text_test.dart +++ b/packages/flutter/test/widgets/editable_text_test.dart @@ -4313,6 +4313,49 @@ void main() { expect(formatter.log, referenceLog); }); + // Regression test for https://github.com/flutter/flutter/issues/53612 + testWidgets('formatter logic handles initial repeat edge case', (WidgetTester tester) async { + final MockTextFormatter formatter = MockTextFormatter(); + await tester.pumpWidget( + MediaQuery( + data: const MediaQueryData(devicePixelRatio: 1.0), + child: Directionality( + textDirection: TextDirection.ltr, + child: FocusScope( + node: focusScopeNode, + autofocus: true, + child: EditableText( + backgroundCursorColor: Colors.grey, + controller: controller, + focusNode: focusNode, + maxLines: 1, // Sets text keyboard implicitly. + style: textStyle, + cursorColor: cursorColor, + inputFormatters: [formatter], + ), + ), + ), + ), + ); + + await tester.tap(find.byType(EditableText)); + await tester.showKeyboard(find.byType(EditableText)); + controller.text = ''; + await tester.idle(); + + final EditableTextState state = + tester.state(find.byType(EditableText)); + expect(tester.testTextInput.editingState['text'], equals('')); + expect(state.wantKeepAlive, true); + + expect(formatter.formatCallCount, 0); + state.updateEditingValue(const TextEditingValue(text: '')); + state.updateEditingValue(const TextEditingValue(text: '', composing: TextRange(start: 1, end: 2))); + state.updateEditingValue(const TextEditingValue(text: '0')); // pass to formatter once to check the values. + expect(formatter.lastOldValue.composing, const TextRange(start: 1, end: 2)); + expect(formatter.lastOldValue.text, ''); + }); + testWidgets('Whitespace directionality formatter input Arabic', (WidgetTester tester) async { final TextEditingController controller = TextEditingController(text: 'testText'); await tester.pumpWidget(