@@ -4715,6 +4715,132 @@ void main() {
47154715 expect (tester.testTextInput.editingState['text' ], 'flutter is the best!...' );
47164716 });
47174717
4718+ testWidgets ('Synchronous test of local and remote editing values' , (WidgetTester tester) async {
4719+ // Regression test for https://github.com/flutter/flutter/issues/65059
4720+ final List <MethodCall > log = < MethodCall > [];
4721+ SystemChannels .textInput.setMockMethodCallHandler ((MethodCall methodCall) async {
4722+ log.add (methodCall);
4723+ });
4724+ final TextInputFormatter formatter = TextInputFormatter .withFunction ((TextEditingValue oldValue, TextEditingValue newValue) {
4725+ if (newValue.text == 'I will be modified by the formatter.' ) {
4726+ newValue = const TextEditingValue (text: 'Flutter is the best!' );
4727+ }
4728+ return newValue;
4729+ });
4730+ final TextEditingController controller = TextEditingController ();
4731+ StateSetter setState;
4732+
4733+ final FocusNode focusNode = FocusNode (debugLabel: 'EditableText Focus Node' );
4734+ Widget builder () {
4735+ return StatefulBuilder (
4736+ builder: (BuildContext context, StateSetter setter) {
4737+ setState = setter;
4738+ return MaterialApp (
4739+ home: MediaQuery (
4740+ data: const MediaQueryData (devicePixelRatio: 1.0 ),
4741+ child: Directionality (
4742+ textDirection: TextDirection .ltr,
4743+ child: Center (
4744+ child: Material (
4745+ child: EditableText (
4746+ controller: controller,
4747+ focusNode: focusNode,
4748+ style: textStyle,
4749+ cursorColor: Colors .red,
4750+ backgroundCursorColor: Colors .red,
4751+ keyboardType: TextInputType .multiline,
4752+ inputFormatters: < TextInputFormatter > [
4753+ formatter,
4754+ ],
4755+ onChanged: (String value) { },
4756+ ),
4757+ ),
4758+ ),
4759+ ),
4760+ ),
4761+ );
4762+ },
4763+ );
4764+ }
4765+
4766+ await tester.pumpWidget (builder ());
4767+ await tester.tap (find.byType (EditableText ));
4768+ await tester.showKeyboard (find.byType (EditableText ));
4769+ await tester.pump ();
4770+
4771+ log.clear ();
4772+
4773+ final EditableTextState state = tester.firstState (find.byType (EditableText ));
4774+
4775+ // setEditingState is not called when only the remote changes
4776+ state.updateEditingValue (const TextEditingValue (
4777+ text: 'a' ,
4778+ ));
4779+ expect (log.length, 0 );
4780+
4781+ // setEditingState is called when remote value modified by the formatter.
4782+ state.updateEditingValue (const TextEditingValue (
4783+ text: 'I will be modified by the formatter.' ,
4784+ ));
4785+ expect (log.length, 1 );
4786+ MethodCall methodCall = log[0 ];
4787+ expect (
4788+ methodCall,
4789+ isMethodCall ('TextInput.setEditingState' , arguments: < String , dynamic > {
4790+ 'text' : 'Flutter is the best!' ,
4791+ 'selectionBase' : - 1 ,
4792+ 'selectionExtent' : - 1 ,
4793+ 'selectionAffinity' : 'TextAffinity.downstream' ,
4794+ 'selectionIsDirectional' : false ,
4795+ 'composingBase' : - 1 ,
4796+ 'composingExtent' : - 1 ,
4797+ }),
4798+ );
4799+
4800+ log.clear ();
4801+
4802+ // setEditingState is called when the [controller.value] is modified by local.
4803+ setState (() {
4804+ controller.text = 'I love flutter!' ;
4805+ });
4806+ expect (log.length, 1 );
4807+ methodCall = log[0 ];
4808+ expect (
4809+ methodCall,
4810+ isMethodCall ('TextInput.setEditingState' , arguments: < String , dynamic > {
4811+ 'text' : 'I love flutter!' ,
4812+ 'selectionBase' : - 1 ,
4813+ 'selectionExtent' : - 1 ,
4814+ 'selectionAffinity' : 'TextAffinity.downstream' ,
4815+ 'selectionIsDirectional' : false ,
4816+ 'composingBase' : - 1 ,
4817+ 'composingExtent' : - 1 ,
4818+ }),
4819+ );
4820+
4821+ log.clear ();
4822+
4823+ // Currently `_receivedRemoteTextEditingValue` equals 'I will be modified by the formatter.',
4824+ // setEditingState will be called when set the [controller.value] to `_receivedRemoteTextEditingValue` by local.
4825+ setState (() {
4826+ controller.text = 'I will be modified by the formatter.' ;
4827+ });
4828+ expect (log.length, 1 );
4829+ methodCall = log[0 ];
4830+ expect (
4831+ methodCall,
4832+ isMethodCall ('TextInput.setEditingState' , arguments: < String , dynamic > {
4833+ 'text' : 'I will be modified by the formatter.' ,
4834+ 'selectionBase' : - 1 ,
4835+ 'selectionExtent' : - 1 ,
4836+ 'selectionAffinity' : 'TextAffinity.downstream' ,
4837+ 'selectionIsDirectional' : false ,
4838+ 'composingBase' : - 1 ,
4839+ 'composingExtent' : - 1 ,
4840+ }),
4841+ );
4842+ });
4843+
47184844 testWidgets ('autofocus:true on first frame does not throw' , (WidgetTester tester) async {
47194845 final TextEditingController controller = TextEditingController (text: testText);
47204846 controller.selection = const TextSelection (
0 commit comments