@@ -4628,41 +4628,136 @@ void main() {
4628
4628
// Ensure selection rects are sent on iPhone (using SE 3rd gen size)
4629
4629
tester.binding.window.physicalSizeTestValue = const Size (750.0 , 1334.0 );
4630
4630
4631
- final List <MethodCall > log = < MethodCall > [];
4631
+ final List <List < SelectionRect >> log = < List < SelectionRect > > [];
4632
4632
SystemChannels .textInput.setMockMethodCallHandler ((MethodCall methodCall) async {
4633
- log.add (methodCall);
4633
+ if (methodCall.method == 'TextInput.setSelectionRects' ) {
4634
+ final List <dynamic > args = methodCall.arguments as List <dynamic >;
4635
+ final List <SelectionRect > selectionRects = < SelectionRect > [];
4636
+ for (final dynamic rect in args) {
4637
+ selectionRects.add (SelectionRect (
4638
+ position: (rect as List <dynamic >)[4 ] as int ,
4639
+ bounds: Rect .fromLTWH (rect[0 ] as double , rect[1 ] as double , rect[2 ] as double , rect[3 ] as double ),
4640
+ ));
4641
+ }
4642
+ log.add (selectionRects);
4643
+ }
4634
4644
});
4635
4645
4636
4646
final TextEditingController controller = TextEditingController ();
4647
+ final ScrollController scrollController = ScrollController ();
4637
4648
controller.text = 'Text1' ;
4638
4649
4639
- await tester.pumpWidget (
4640
- MediaQuery (
4641
- data: const MediaQueryData (),
4642
- child: Directionality (
4643
- textDirection: TextDirection .ltr,
4644
- child: Column (
4645
- crossAxisAlignment: CrossAxisAlignment .start,
4646
- children: < Widget > [
4647
- EditableText (
4648
- key: ValueKey <String >(controller.text),
4649
- controller: controller,
4650
- focusNode: FocusNode (),
4651
- style: Typography .material2018 ().black.titleMedium! ,
4652
- cursorColor: Colors .blue,
4653
- backgroundCursorColor: Colors .grey,
4650
+ Future <void > pumpEditableText ({ double ? width, double ? height, TextAlign textAlign = TextAlign .start }) async {
4651
+ await tester.pumpWidget (
4652
+ MediaQuery (
4653
+ data: const MediaQueryData (),
4654
+ child: Directionality (
4655
+ textDirection: TextDirection .ltr,
4656
+ child: Center (
4657
+ child: SizedBox (
4658
+ width: width,
4659
+ height: height,
4660
+ child: EditableText (
4661
+ controller: controller,
4662
+ textAlign: textAlign,
4663
+ scrollController: scrollController,
4664
+ maxLines: null ,
4665
+ focusNode: focusNode,
4666
+ cursorWidth: 0 ,
4667
+ style: Typography .material2018 ().black.titleMedium! ,
4668
+ cursorColor: Colors .blue,
4669
+ backgroundCursorColor: Colors .grey,
4670
+ ),
4654
4671
),
4655
- ] ,
4672
+ ) ,
4656
4673
),
4657
4674
),
4658
- ),
4659
- );
4660
- await tester.showKeyboard (find.byKey (ValueKey <String >(controller.text)));
4675
+ );
4676
+ }
4661
4677
4662
- // There should be a new platform message updating the selection rects.
4663
- final MethodCall methodCall = log.firstWhere ((MethodCall m) => m.method == 'TextInput.setSelectionRects' );
4664
- expect (methodCall.method, 'TextInput.setSelectionRects' );
4665
- expect ((methodCall.arguments as List <dynamic >).length, 5 );
4678
+ await pumpEditableText ();
4679
+ expect (log, isEmpty);
4680
+
4681
+ await tester.showKeyboard (find.byType (EditableText ));
4682
+ // First update.
4683
+ expect (log.single, const < SelectionRect > [
4684
+ SelectionRect (position: 0 , bounds: Rect .fromLTRB (0.0 , 0.0 , 14.0 , 14.0 )),
4685
+ SelectionRect (position: 1 , bounds: Rect .fromLTRB (14.0 , 0.0 , 28.0 , 14.0 )),
4686
+ SelectionRect (position: 2 , bounds: Rect .fromLTRB (28.0 , 0.0 , 42.0 , 14.0 )),
4687
+ SelectionRect (position: 3 , bounds: Rect .fromLTRB (42.0 , 0.0 , 56.0 , 14.0 )),
4688
+ SelectionRect (position: 4 , bounds: Rect .fromLTRB (56.0 , 0.0 , 70.0 , 14.0 ))
4689
+ ]);
4690
+ log.clear ();
4691
+
4692
+ await tester.pumpAndSettle ();
4693
+ expect (log, isEmpty);
4694
+
4695
+ await pumpEditableText ();
4696
+ expect (log, isEmpty);
4697
+
4698
+ // Change the width such that each character occupies a line.
4699
+ await pumpEditableText (width: 20 );
4700
+ expect (log.single, const < SelectionRect > [
4701
+ SelectionRect (position: 0 , bounds: Rect .fromLTRB (0.0 , 0.0 , 14.0 , 14.0 )),
4702
+ SelectionRect (position: 1 , bounds: Rect .fromLTRB (0.0 , 14.0 , 14.0 , 28.0 )),
4703
+ SelectionRect (position: 2 , bounds: Rect .fromLTRB (0.0 , 28.0 , 14.0 , 42.0 )),
4704
+ SelectionRect (position: 3 , bounds: Rect .fromLTRB (0.0 , 42.0 , 14.0 , 56.0 )),
4705
+ SelectionRect (position: 4 , bounds: Rect .fromLTRB (0.0 , 56.0 , 14.0 , 70.0 ))
4706
+ ]);
4707
+ log.clear ();
4708
+
4709
+ await tester.enterText (find.byType (EditableText ), 'Text1👨👩👦' );
4710
+ await tester.pump ();
4711
+ expect (log.single, const < SelectionRect > [
4712
+ SelectionRect (position: 0 , bounds: Rect .fromLTRB (0.0 , 0.0 , 14.0 , 14.0 )),
4713
+ SelectionRect (position: 1 , bounds: Rect .fromLTRB (0.0 , 14.0 , 14.0 , 28.0 )),
4714
+ SelectionRect (position: 2 , bounds: Rect .fromLTRB (0.0 , 28.0 , 14.0 , 42.0 )),
4715
+ SelectionRect (position: 3 , bounds: Rect .fromLTRB (0.0 , 42.0 , 14.0 , 56.0 )),
4716
+ SelectionRect (position: 4 , bounds: Rect .fromLTRB (0.0 , 56.0 , 14.0 , 70.0 )),
4717
+ SelectionRect (position: 5 , bounds: Rect .fromLTRB (0.0 , 70.0 , 42.0 , 84.0 )),
4718
+ ]);
4719
+ log.clear ();
4720
+
4721
+ // The 4th line will be partially visible.
4722
+ await pumpEditableText (width: 20 , height: 45 );
4723
+ expect (log.single, const < SelectionRect > [
4724
+ SelectionRect (position: 0 , bounds: Rect .fromLTRB (0.0 , 0.0 , 14.0 , 14.0 )),
4725
+ SelectionRect (position: 1 , bounds: Rect .fromLTRB (0.0 , 14.0 , 14.0 , 28.0 )),
4726
+ SelectionRect (position: 2 , bounds: Rect .fromLTRB (0.0 , 28.0 , 14.0 , 42.0 )),
4727
+ SelectionRect (position: 3 , bounds: Rect .fromLTRB (0.0 , 42.0 , 14.0 , 56.0 )),
4728
+ ]);
4729
+ log.clear ();
4730
+
4731
+ await pumpEditableText (width: 20 , height: 45 , textAlign: TextAlign .right);
4732
+ // This is 1px off from being completely right-aligned. The 1px width is
4733
+ // reserved for caret.
4734
+ expect (log.single, const < SelectionRect > [
4735
+ SelectionRect (position: 0 , bounds: Rect .fromLTRB (5.0 , 0.0 , 19.0 , 14.0 )),
4736
+ SelectionRect (position: 1 , bounds: Rect .fromLTRB (5.0 , 14.0 , 19.0 , 28.0 )),
4737
+ SelectionRect (position: 2 , bounds: Rect .fromLTRB (5.0 , 28.0 , 19.0 , 42.0 )),
4738
+ SelectionRect (position: 3 , bounds: Rect .fromLTRB (5.0 , 42.0 , 19.0 , 56.0 )),
4739
+ // These 2 lines will be out of bounds.
4740
+ // SelectionRect(position: 4, bounds: Rect.fromLTRB(5.0, 56.0, 19.0, 70.0)),
4741
+ // SelectionRect(position: 5, bounds: Rect.fromLTRB(-23.0, 70.0, 19.0, 84.0)),
4742
+ ]);
4743
+ log.clear ();
4744
+
4745
+ expect (scrollController.offset, 0 );
4746
+
4747
+ // Scrolling also triggers update.
4748
+ scrollController.jumpTo (14 );
4749
+ await tester.pumpAndSettle ();
4750
+ expect (log.single, const < SelectionRect > [
4751
+ SelectionRect (position: 0 , bounds: Rect .fromLTRB (5.0 , - 14.0 , 19.0 , 0.0 )),
4752
+ SelectionRect (position: 1 , bounds: Rect .fromLTRB (5.0 , 0.0 , 19.0 , 14.0 )),
4753
+ SelectionRect (position: 2 , bounds: Rect .fromLTRB (5.0 , 14.0 , 19.0 , 28.0 )),
4754
+ SelectionRect (position: 3 , bounds: Rect .fromLTRB (5.0 , 28.0 , 19.0 , 42.0 )),
4755
+ SelectionRect (position: 4 , bounds: Rect .fromLTRB (5.0 , 42.0 , 19.0 , 56.0 )),
4756
+ // This line is skipped because it's below the bottom edge of the render
4757
+ // object.
4758
+ // SelectionRect(position: 5, bounds: Rect.fromLTRB(5.0, 56.0, 47.0, 70.0)),
4759
+ ]);
4760
+ log.clear ();
4666
4761
4667
4762
// On web, we should rely on the browser's implementation of Scribble, so we will not send selection rects.
4668
4763
}, skip: kIsWeb, variant: const TargetPlatformVariant (< TargetPlatform > { TargetPlatform .iOS })); // [intended]
0 commit comments