Fix TextField bug when the formatter repeatedly format (#67892)

This commit is contained in:
xubaolin 2020-10-13 00:37:03 +08:00 committed by GitHub
parent 345188d40e
commit 8538b4f546
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 87 additions and 2 deletions

View file

@ -2212,8 +2212,15 @@ class EditableTextState extends State<EditableText> 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!;

View file

@ -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<MethodCall> log = <MethodCall>[];
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: <TextInputFormatter>[
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(