From 2931e50c30d9bc21b88529fe53f69be5362471b0 Mon Sep 17 00:00:00 2001 From: Justin McCandless Date: Wed, 21 Dec 2022 15:26:19 -0800 Subject: [PATCH] Handle the case of no selection rects (#117419) Fixes an error that can occur when selection contains a partial glyph. --- .../lib/src/widgets/editable_text.dart | 11 ++++- .../test/widgets/editable_text_test.dart | 44 +++++++++++++++++++ 2 files changed, 53 insertions(+), 2 deletions(-) diff --git a/packages/flutter/lib/src/widgets/editable_text.dart b/packages/flutter/lib/src/widgets/editable_text.dart index 990574f18ba4..b7c3f2778411 100644 --- a/packages/flutter/lib/src/widgets/editable_text.dart +++ b/packages/flutter/lib/src/widgets/editable_text.dart @@ -3266,8 +3266,15 @@ class EditableTextState extends State with AutomaticKeepAliveClien rectToReveal = targetOffset.rect; } else { final List selectionBoxes = renderEditable.getBoxesForSelection(selection); - rectToReveal = selection.baseOffset < selection.extentOffset ? - selectionBoxes.last : selectionBoxes.first; + // selectionBoxes may be empty if, for example, the selection does not + // encompass a full character, like if it only contained part of an + // extended grapheme cluster. + if (selectionBoxes.isEmpty) { + rectToReveal = targetOffset.rect; + } else { + rectToReveal = selection.baseOffset < selection.extentOffset ? + selectionBoxes.last : selectionBoxes.first; + } } if (withAnimation) { diff --git a/packages/flutter/test/widgets/editable_text_test.dart b/packages/flutter/test/widgets/editable_text_test.dart index f8b141acb455..4cc4d13ff9d7 100644 --- a/packages/flutter/test/widgets/editable_text_test.dart +++ b/packages/flutter/test/widgets/editable_text_test.dart @@ -14354,6 +14354,50 @@ testWidgets('Floating cursor ending with selection', (WidgetTester tester) async ); }); }); + + // Regression test for: https://github.com/flutter/flutter/issues/117418. + testWidgets('can handle the partial selection of a multi-code-unit glyph', (WidgetTester tester) async { + await tester.pumpWidget( + MaterialApp( + home: EditableText( + controller: controller, + showSelectionHandles: true, + autofocus: true, + focusNode: FocusNode(), + style: Typography.material2018().black.titleMedium!, + cursorColor: Colors.blue, + backgroundCursorColor: Colors.grey, + selectionControls: materialTextSelectionControls, + keyboardType: TextInputType.text, + textAlign: TextAlign.right, + minLines: 2, + maxLines: 2, + ), + ), + ); + + await tester.enterText(find.byType(EditableText), '12345'); + await tester.pumpAndSettle(); + + final EditableTextState state = + tester.state(find.byType(EditableText)); + state.userUpdateTextEditingValue( + const TextEditingValue( + // This is an extended grapheme cluster made up of several code units, + // which has length 8. A selection from 0-1 does not fully select it. + text: '👨‍👩‍👦', + selection: TextSelection( + baseOffset: 0, + extentOffset: 1, + ), + ), + SelectionChangedCause.keyboard, + ); + + await tester.pumpAndSettle(); + + expect(tester.takeException(), null); + }); } class UnsettableController extends TextEditingController {