Unverified Commit 88852731 authored by Per Classon's avatar Per Classon Committed by GitHub

Validate text selection before updating it inside of EditableText (#51410)

parent c4525aeb
......@@ -189,7 +189,7 @@ class TextEditingController extends ValueNotifier<TextEditingValue> {
/// in a separate statement. To change both the [text] and the [selection]
/// change the controller's [value].
set selection(TextSelection newSelection) {
if (newSelection.start > text.length || newSelection.end > text.length)
if (!isSelectionWithinTextBounds(newSelection))
throw FlutterError('invalid text selection: $newSelection');
value = value.copyWith(selection: newSelection, composing: TextRange.empty);
}
......@@ -220,6 +220,11 @@ class TextEditingController extends ValueNotifier<TextEditingValue> {
void clearComposing() {
value = value.copyWith(composing: TextRange.empty);
}
/// Check that the [selection] is inside of the bounds of [text].
bool isSelectionWithinTextBounds(TextSelection selection) {
return selection.start <= text.length && selection.end <= text.length;
}
}
/// Toolbar configuration for [EditableText].
......@@ -1530,6 +1535,12 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
}
void _handleSelectionChanged(TextSelection selection, RenderEditable renderObject, SelectionChangedCause cause) {
// We return early if the selection is not valid. This can happen when the
// text of [EditableText] is updated at the same time as the selection is
// changed by a gesture event.
if (!widget.controller.isSelectionWithinTextBounds(selection))
return;
widget.controller.selection = selection;
// This will show the keyboard for all selection changes on the
......
......@@ -3378,6 +3378,30 @@ void main() {
}
});
// Regression test for https://github.com/flutter/flutter/issues/35848
testWidgets('Clearing text field with suffixIcon does not cause text selection exception', (WidgetTester tester) async {
final TextEditingController controller = TextEditingController(
text: 'Prefilled text.',
);
await tester.pumpWidget(
boilerplate(
child: TextField(
controller: controller,
decoration: InputDecoration(
suffixIcon: IconButton(
icon: const Icon(Icons.close),
onPressed: controller.clear,
),
),
),
),
);
await tester.tap(find.byType(IconButton));
expect(controller.text, '');
});
testWidgets('maxLength limits input.', (WidgetTester tester) async {
final TextEditingController textController = TextEditingController();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment