Skip to content

Commit 39becb7

Browse files
authored
iOS spell check cursor placement (#124875)
Fixes the cursor location after selecting a spell check result on iOS.
1 parent f7245b6 commit 39becb7

2 files changed

Lines changed: 122 additions & 5 deletions

File tree

packages/flutter/lib/src/cupertino/spell_check_suggestions_toolbar.dart

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -98,10 +98,16 @@ class CupertinoSpellCheckSuggestionsToolbar extends StatelessWidget {
9898
// Replacement cannot be performed if the text is read only or obscured.
9999
assert(!editableTextState.widget.readOnly && !editableTextState.widget.obscureText);
100100

101-
final TextEditingValue newValue = editableTextState.textEditingValue.replaced(
102-
replacementRange,
103-
text,
104-
);
101+
final TextEditingValue newValue = editableTextState.textEditingValue
102+
.replaced(
103+
replacementRange,
104+
text,
105+
)
106+
.copyWith(
107+
selection: TextSelection.collapsed(
108+
offset: replacementRange.start + text.length,
109+
),
110+
);
105111
editableTextState.userUpdateTextEditingValue(newValue,SelectionChangedCause.toolbar);
106112

107113
// Schedule a call to bringIntoView() after renderEditable updates.
@@ -111,7 +117,6 @@ class CupertinoSpellCheckSuggestionsToolbar extends StatelessWidget {
111117
}
112118
});
113119
editableTextState.hideToolbar();
114-
editableTextState.renderEditable.selectWordEdge(cause: SelectionChangedCause.toolbar);
115120
}
116121

117122
/// Builds the toolbar buttons based on the [buttonItems].

packages/flutter/test/widgets/editable_text_test.dart

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15294,6 +15294,118 @@ testWidgets('Floating cursor ending with selection', (WidgetTester tester) async
1529415294
expect(state.currentTextEditingValue.selection.baseOffset, equals(0));
1529515295
}
1529615296
});
15297+
15298+
testWidgets('replacing puts cursor at the end of the word', (WidgetTester tester) async {
15299+
tester.binding.platformDispatcher.nativeSpellCheckServiceDefinedTestValue =
15300+
true;
15301+
controller.value = const TextEditingValue(
15302+
// All misspellings of "test". One the same length, one shorter, and one
15303+
// longer.
15304+
text: 'tset tst testt',
15305+
selection: TextSelection(affinity: TextAffinity.upstream, baseOffset: 0, extentOffset: 4),
15306+
);
15307+
await tester.pumpWidget(
15308+
CupertinoApp(
15309+
home: EditableText(
15310+
backgroundCursorColor: Colors.grey,
15311+
controller: controller,
15312+
focusNode: focusNode,
15313+
style: textStyle,
15314+
cursorColor: cursorColor,
15315+
selectionControls: materialTextSelectionControls,
15316+
spellCheckConfiguration:
15317+
const SpellCheckConfiguration(
15318+
misspelledTextStyle: CupertinoTextField.cupertinoMisspelledTextStyle,
15319+
spellCheckSuggestionsToolbarBuilder: CupertinoTextField.defaultSpellCheckSuggestionsToolbarBuilder,
15320+
),
15321+
),
15322+
),
15323+
);
15324+
15325+
final EditableTextState state =
15326+
tester.state<EditableTextState>(find.byType(EditableText));
15327+
15328+
state.spellCheckResults = SpellCheckResults(
15329+
controller.value.text,
15330+
const <SuggestionSpan>[
15331+
SuggestionSpan(TextRange(start: 0, end: 4), <String>['test']),
15332+
SuggestionSpan(TextRange(start: 5, end: 8), <String>['test']),
15333+
SuggestionSpan(TextRange(start: 9, end: 13), <String>['test']),
15334+
]);
15335+
await tester.tapAt(textOffsetToPosition(tester, 0));
15336+
await tester.pumpAndSettle();
15337+
expect(state.showSpellCheckSuggestionsToolbar(), isTrue);
15338+
await tester.pumpAndSettle();
15339+
expect(find.text('test'), findsOneWidget);
15340+
15341+
// Replacing a word of the same length as the replacement puts the cursor
15342+
// at the end of the new word.
15343+
await tester.tap(find.text('test'));
15344+
await tester.pumpAndSettle();
15345+
expect(
15346+
controller.value,
15347+
equals(const TextEditingValue(
15348+
text: 'test tst testt',
15349+
selection: TextSelection.collapsed(
15350+
offset: 4,
15351+
),
15352+
)),
15353+
);
15354+
15355+
state.spellCheckResults = SpellCheckResults(
15356+
controller.value.text,
15357+
const <SuggestionSpan>[
15358+
SuggestionSpan(TextRange(start: 5, end: 8), <String>['test']),
15359+
SuggestionSpan(TextRange(start: 9, end: 13), <String>['test']),
15360+
]);
15361+
await tester.tapAt(textOffsetToPosition(tester, 5));
15362+
await tester.pumpAndSettle();
15363+
expect(state.showSpellCheckSuggestionsToolbar(), isTrue);
15364+
await tester.pumpAndSettle();
15365+
expect(find.text('test'), findsOneWidget);
15366+
15367+
// Replacing a word of less length as the replacement puts the cursor at
15368+
// the end of the new word.
15369+
await tester.tap(find.text('test'));
15370+
await tester.pumpAndSettle();
15371+
expect(
15372+
controller.value,
15373+
equals(const TextEditingValue(
15374+
text: 'test test testt',
15375+
selection: TextSelection.collapsed(
15376+
offset: 9,
15377+
),
15378+
)),
15379+
);
15380+
15381+
state.spellCheckResults = SpellCheckResults(
15382+
controller.value.text,
15383+
const <SuggestionSpan>[
15384+
SuggestionSpan(TextRange(start: 10, end: 15), <String>['test']),
15385+
]);
15386+
await tester.tapAt(textOffsetToPosition(tester, 10));
15387+
await tester.pumpAndSettle();
15388+
expect(state.showSpellCheckSuggestionsToolbar(), isTrue);
15389+
await tester.pumpAndSettle();
15390+
expect(find.text('test'), findsOneWidget);
15391+
15392+
// Replacing a word of greater length as the replacement puts the cursor
15393+
// at the end of the new word.
15394+
await tester.tap(find.text('test'));
15395+
await tester.pumpAndSettle();
15396+
expect(
15397+
controller.value,
15398+
equals(const TextEditingValue(
15399+
text: 'test test test',
15400+
selection: TextSelection.collapsed(
15401+
offset: 14,
15402+
),
15403+
)),
15404+
);
15405+
},
15406+
variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.android }),
15407+
skip: kIsWeb, // [intended]
15408+
);
1529715409
});
1529815410

1529915411
group('magnifier', () {

0 commit comments

Comments
 (0)