@@ -15975,6 +15975,204 @@ void main() {
1597515975 );
1597615976 });
1597715977
15978+ testWidgets('Spell check disabled when obscureText is true', (WidgetTester tester) async {
15979+ final fakeSpellCheckService = FakeSpellCheckService();
15980+ controller.text = 'A';
15981+
15982+ await tester.pumpWidget(
15983+ TestWidgetsApp(
15984+ home: EditableText(
15985+ controller: controller,
15986+ focusNode: focusNode,
15987+ obscureText: true,
15988+ style: const TextStyle(),
15989+ cursorColor: const Color(0xFF0000FF),
15990+ backgroundCursorColor: const Color(0xFF808080),
15991+ cursorOpacityAnimates: true,
15992+ autofillHints: null,
15993+ spellCheckConfiguration: SpellCheckConfiguration(
15994+ spellCheckService: fakeSpellCheckService,
15995+ misspelledTextStyle: const TextStyle(decoration: TextDecoration.underline),
15996+ ),
15997+ ),
15998+ ),
15999+ );
16000+
16001+ final EditableTextState state = tester.state<EditableTextState>(find.byType(EditableText));
16002+ expect(state.spellCheckEnabled, isFalse);
16003+ expect(state.spellCheckConfiguration, equals(const SpellCheckConfiguration.disabled()));
16004+ });
16005+
16006+ testWidgets('Spell check disabled for visible password input type', (
16007+ WidgetTester tester,
16008+ ) async {
16009+ final fakeSpellCheckService = FakeSpellCheckService();
16010+ controller.text = 'A';
16011+
16012+ await tester.pumpWidget(
16013+ TestWidgetsApp(
16014+ home: EditableText(
16015+ controller: controller,
16016+ focusNode: focusNode,
16017+ keyboardType: TextInputType.visiblePassword,
16018+ style: const TextStyle(),
16019+ cursorColor: const Color(0xFF0000FF),
16020+ backgroundCursorColor: const Color(0xFF808080),
16021+ cursorOpacityAnimates: true,
16022+ autofillHints: null,
16023+ spellCheckConfiguration: SpellCheckConfiguration(
16024+ spellCheckService: fakeSpellCheckService,
16025+ misspelledTextStyle: const TextStyle(decoration: TextDecoration.underline),
16026+ ),
16027+ ),
16028+ ),
16029+ );
16030+
16031+ final EditableTextState state = tester.state<EditableTextState>(find.byType(EditableText));
16032+ expect(state.spellCheckEnabled, isFalse);
16033+ expect(state.spellCheckConfiguration, equals(const SpellCheckConfiguration.disabled()));
16034+ });
16035+
16036+ testWidgets('Spell check disabled for password autofill hints', (WidgetTester tester) async {
16037+ final fakeSpellCheckService = FakeSpellCheckService();
16038+ controller.text = 'A';
16039+
16040+ await tester.pumpWidget(
16041+ TestWidgetsApp(
16042+ home: EditableText(
16043+ controller: controller,
16044+ focusNode: focusNode,
16045+ style: const TextStyle(),
16046+ cursorColor: const Color(0xFF0000FF),
16047+ backgroundCursorColor: const Color(0xFF808080),
16048+ cursorOpacityAnimates: true,
16049+ autofillHints: const <String>[AutofillHints.password],
16050+ spellCheckConfiguration: SpellCheckConfiguration(
16051+ spellCheckService: fakeSpellCheckService,
16052+ misspelledTextStyle: const TextStyle(decoration: TextDecoration.underline),
16053+ ),
16054+ ),
16055+ ),
16056+ );
16057+
16058+ final EditableTextState state = tester.state<EditableTextState>(find.byType(EditableText));
16059+ expect(state.spellCheckEnabled, isFalse);
16060+ expect(state.spellCheckConfiguration, equals(const SpellCheckConfiguration.disabled()));
16061+ });
16062+
16063+ testWidgets('Spell check updates when non-password obscureText changes', (
16064+ WidgetTester tester,
16065+ ) async {
16066+ const suggestionSpans = <SuggestionSpan>[
16067+ SuggestionSpan(TextRange(start: 0, end: 1), <String>['a']),
16068+ ];
16069+ final fakeSpellCheckService = FakeSpellCheckService(
16070+ suggestionSpansByText: const <String, List<SuggestionSpan>?>{'A': suggestionSpans},
16071+ );
16072+ controller.text = 'A';
16073+ var obscureText = false;
16074+ late StateSetter setState;
16075+
16076+ await tester.pumpWidget(
16077+ TestWidgetsApp(
16078+ home: StatefulBuilder(
16079+ builder: (BuildContext context, StateSetter localSetState) {
16080+ setState = localSetState;
16081+ return EditableText(
16082+ controller: controller,
16083+ focusNode: focusNode,
16084+ obscureText: obscureText,
16085+ style: const TextStyle(),
16086+ cursorColor: const Color(0xFF0000FF),
16087+ backgroundCursorColor: const Color(0xFF808080),
16088+ cursorOpacityAnimates: true,
16089+ autofillHints: null,
16090+ spellCheckConfiguration: SpellCheckConfiguration(
16091+ spellCheckService: fakeSpellCheckService,
16092+ misspelledTextStyle: const TextStyle(decoration: TextDecoration.underline),
16093+ ),
16094+ );
16095+ },
16096+ ),
16097+ ),
16098+ );
16099+
16100+ void setObscureText(bool value) {
16101+ setState(() {
16102+ obscureText = value;
16103+ });
16104+ }
16105+
16106+ EditableTextState state = tester.state<EditableTextState>(find.byType(EditableText));
16107+ expect(state.spellCheckEnabled, isTrue);
16108+ state.spellCheckResults = const SpellCheckResults('A', suggestionSpans);
16109+
16110+ setObscureText(true);
16111+ await tester.pump();
16112+
16113+ state = tester.state<EditableTextState>(find.byType(EditableText));
16114+ expect(state.spellCheckEnabled, isFalse);
16115+ expect(state.spellCheckConfiguration, equals(const SpellCheckConfiguration.disabled()));
16116+ expect(state.spellCheckResults, isNull);
16117+ expect(fakeSpellCheckService.fetchSpellCheckSuggestionsCallCount, 0);
16118+
16119+ setObscureText(false);
16120+ await tester.pump();
16121+
16122+ state = tester.state<EditableTextState>(find.byType(EditableText));
16123+ expect(state.spellCheckEnabled, isTrue);
16124+ expect(fakeSpellCheckService.fetchSpellCheckSuggestionsCallCount, 1);
16125+ expect(fakeSpellCheckService.lastSpellCheckText, 'A');
16126+ expect(state.spellCheckResults, const SpellCheckResults('A', suggestionSpans));
16127+ });
16128+
16129+ testWidgets('Spell check stays disabled for visible password when obscureText changes', (
16130+ WidgetTester tester,
16131+ ) async {
16132+ final fakeSpellCheckService = FakeSpellCheckService();
16133+ controller.text = 'A';
16134+ var obscureText = true;
16135+ late StateSetter setState;
16136+
16137+ await tester.pumpWidget(
16138+ TestWidgetsApp(
16139+ home: StatefulBuilder(
16140+ builder: (BuildContext context, StateSetter localSetState) {
16141+ setState = localSetState;
16142+ return EditableText(
16143+ controller: controller,
16144+ focusNode: focusNode,
16145+ keyboardType: TextInputType.visiblePassword,
16146+ obscureText: obscureText,
16147+ style: const TextStyle(),
16148+ cursorColor: const Color(0xFF0000FF),
16149+ backgroundCursorColor: const Color(0xFF808080),
16150+ cursorOpacityAnimates: true,
16151+ autofillHints: null,
16152+ spellCheckConfiguration: SpellCheckConfiguration(
16153+ spellCheckService: fakeSpellCheckService,
16154+ misspelledTextStyle: const TextStyle(decoration: TextDecoration.underline),
16155+ ),
16156+ );
16157+ },
16158+ ),
16159+ ),
16160+ );
16161+
16162+ EditableTextState state = tester.state<EditableTextState>(find.byType(EditableText));
16163+ expect(state.spellCheckEnabled, isFalse);
16164+
16165+ setState(() {
16166+ obscureText = false;
16167+ });
16168+ await tester.pump();
16169+
16170+ state = tester.state<EditableTextState>(find.byType(EditableText));
16171+ expect(state.spellCheckEnabled, isFalse);
16172+ expect(state.spellCheckConfiguration, equals(const SpellCheckConfiguration.disabled()));
16173+ expect(fakeSpellCheckService.fetchSpellCheckSuggestionsCallCount, 0);
16174+ });
16175+
1597816176 testWidgets(
1597916177 'Spell check disabled when spell check configuration specified but no default spell check service available',
1598016178 (WidgetTester tester) async {
@@ -19019,7 +19217,22 @@ class _TestScrollController extends ScrollController {
1901919217 bool get attached => hasListeners;
1902019218}
1902119219
19022- class FakeSpellCheckService extends DefaultSpellCheckService {}
19220+ class FakeSpellCheckService extends DefaultSpellCheckService {
19221+ FakeSpellCheckService({this.suggestionSpansByText = const <String, List<SuggestionSpan>?>{}});
19222+
19223+ final Map<String, List<SuggestionSpan>?> suggestionSpansByText;
19224+ int fetchSpellCheckSuggestionsCallCount = 0;
19225+ String? lastSpellCheckText;
19226+
19227+ @override
19228+ Future<List<SuggestionSpan>?> fetchSpellCheckSuggestions(Locale locale, String text) async {
19229+ fetchSpellCheckSuggestionsCallCount += 1;
19230+ lastSpellCheckText = text;
19231+ return suggestionSpansByText.containsKey(text)
19232+ ? suggestionSpansByText[text]
19233+ : const <SuggestionSpan>[];
19234+ }
19235+ }
1902319236
1902419237class FakeFlutterView extends TestFlutterView {
1902519238 FakeFlutterView(TestFlutterView view, {required this.viewId})
0 commit comments