diff --git a/packages/flutter/lib/src/cupertino/search_field.dart b/packages/flutter/lib/src/cupertino/search_field.dart index a6e4fbefeb0..aba27885876 100644 --- a/packages/flutter/lib/src/cupertino/search_field.dart +++ b/packages/flutter/lib/src/cupertino/search_field.dart @@ -274,7 +274,9 @@ class CupertinoSearchTextField extends StatefulWidget { /// {@macro flutter.material.textfield.onTap} final VoidCallback? onTap; - /// {@macro flutter.widgets.editableText.autocorrect} + /// Whether to enable autocorrection. + /// + /// Defaults to true. final bool autocorrect; /// Whether to allow the platform to automatically format quotes. diff --git a/packages/flutter/lib/src/cupertino/text_field.dart b/packages/flutter/lib/src/cupertino/text_field.dart index a682133bb06..8932bd1ec77 100644 --- a/packages/flutter/lib/src/cupertino/text_field.dart +++ b/packages/flutter/lib/src/cupertino/text_field.dart @@ -400,7 +400,7 @@ class CupertinoTextField extends StatefulWidget { this.autofocus = false, this.obscuringCharacter = '•', this.obscureText = false, - this.autocorrect = true, + this.autocorrect, SmartDashesType? smartDashesType, SmartQuotesType? smartQuotesType, this.enableSuggestions = true, @@ -619,7 +619,7 @@ class CupertinoTextField extends StatefulWidget { final bool obscureText; /// {@macro flutter.widgets.editableText.autocorrect} - final bool autocorrect; + final bool? autocorrect; /// {@macro flutter.services.TextInputConfiguration.smartDashesType} final SmartDashesType smartDashesType; @@ -920,7 +920,7 @@ class CupertinoTextField extends StatefulWidget { DiagnosticsProperty('obscuringCharacter', obscuringCharacter, defaultValue: '•'), ); properties.add(DiagnosticsProperty('obscureText', obscureText, defaultValue: false)); - properties.add(DiagnosticsProperty('autocorrect', autocorrect, defaultValue: true)); + properties.add(DiagnosticsProperty('autocorrect', autocorrect, defaultValue: null)); properties.add( EnumProperty( 'smartDashesType', diff --git a/packages/flutter/lib/src/material/search.dart b/packages/flutter/lib/src/material/search.dart index 065dda5d68d..dd3dbdb288b 100644 --- a/packages/flutter/lib/src/material/search.dart +++ b/packages/flutter/lib/src/material/search.dart @@ -370,7 +370,9 @@ abstract class SearchDelegate { /// Defaults to the default value specified in [TextField]. final TextInputType? keyboardType; - /// {@macro flutter.widgets.editableText.autocorrect} + /// Whether to enable autocorrection. + /// + /// Defaults to true. final bool autocorrect; /// {@macro flutter.services.TextInputConfiguration.enableSuggestions} diff --git a/packages/flutter/lib/src/material/text_field.dart b/packages/flutter/lib/src/material/text_field.dart index 3301ef0f10d..02977f41630 100644 --- a/packages/flutter/lib/src/material/text_field.dart +++ b/packages/flutter/lib/src/material/text_field.dart @@ -264,7 +264,7 @@ class TextField extends StatefulWidget { this.statesController, this.obscuringCharacter = '•', this.obscureText = false, - this.autocorrect = true, + this.autocorrect, SmartDashesType? smartDashesType, SmartQuotesType? smartQuotesType, this.enableSuggestions = true, @@ -485,7 +485,7 @@ class TextField extends StatefulWidget { final bool obscureText; /// {@macro flutter.widgets.editableText.autocorrect} - final bool autocorrect; + final bool? autocorrect; /// {@macro flutter.services.TextInputConfiguration.smartDashesType} final SmartDashesType smartDashesType; @@ -961,7 +961,7 @@ class TextField extends StatefulWidget { DiagnosticsProperty('obscuringCharacter', obscuringCharacter, defaultValue: '•'), ); properties.add(DiagnosticsProperty('obscureText', obscureText, defaultValue: false)); - properties.add(DiagnosticsProperty('autocorrect', autocorrect, defaultValue: true)); + properties.add(DiagnosticsProperty('autocorrect', autocorrect, defaultValue: null)); properties.add( EnumProperty( 'smartDashesType', @@ -1695,6 +1695,7 @@ class _TextFieldState extends State dragStartBehavior: widget.dragStartBehavior, scrollController: widget.scrollController, scrollPhysics: widget.scrollPhysics, + autofillHints: widget.autofillHints, autofillClient: this, autocorrectionTextRectColor: autocorrectionTextRectColor, clipBehavior: widget.clipBehavior, diff --git a/packages/flutter/lib/src/widgets/editable_text.dart b/packages/flutter/lib/src/widgets/editable_text.dart index 3dbcaaf2c5e..0fcd23d2fe7 100644 --- a/packages/flutter/lib/src/widgets/editable_text.dart +++ b/packages/flutter/lib/src/widgets/editable_text.dart @@ -826,7 +826,7 @@ class EditableText extends StatefulWidget { this.readOnly = false, this.obscuringCharacter = '•', this.obscureText = false, - this.autocorrect = true, + bool? autocorrect, SmartDashesType? smartDashesType, SmartQuotesType? smartQuotesType, this.enableSuggestions = true, @@ -908,6 +908,7 @@ class EditableText extends StatefulWidget { this.magnifierConfiguration = TextMagnifierConfiguration.disabled, this.undoController, }) : assert(obscuringCharacter.length == 1), + autocorrect = autocorrect ?? _inferAutocorrect(autofillHints: autofillHints), smartDashesType = smartDashesType ?? (obscureText ? SmartDashesType.disabled : SmartDashesType.enabled), smartQuotesType = @@ -1050,7 +1051,7 @@ class EditableText extends StatefulWidget { /// {@template flutter.widgets.editableText.autocorrect} /// Whether to enable autocorrection. /// - /// Defaults to true. + /// False on iOS if [autofillHints] contains password-related hints, otherwise true. /// {@endtemplate} final bool autocorrect; @@ -2108,6 +2109,38 @@ class EditableText extends StatefulWidget { return resultButtonItem; } + // Infer the value of autocorrect from autofillHints. + static bool _inferAutocorrect({required Iterable? autofillHints}) { + if (autofillHints == null || autofillHints.isEmpty || kIsWeb) { + return true; + } + + switch (defaultTargetPlatform) { + case TargetPlatform.iOS: + // username, password and newPassword are password related hint. + // newUsername is not supported on iOS. + final bool passwordRelatedHint = autofillHints.any( + (String hint) => + hint == AutofillHints.username || + hint == AutofillHints.password || + hint == AutofillHints.newPassword, + ); + if (passwordRelatedHint) { + // https://github.com/flutter/flutter/issues/134723 + // Set autocorrect to false to prevent password bar from flashing. + return false; + } + case TargetPlatform.macOS: + case TargetPlatform.android: + case TargetPlatform.fuchsia: + case TargetPlatform.linux: + case TargetPlatform.windows: + break; + } + + return true; + } + // Infer the keyboard type of an `EditableText` if it's not specified. static TextInputType _inferKeyboardType({ required Iterable? autofillHints, @@ -2260,7 +2293,7 @@ class EditableText extends StatefulWidget { properties.add(DiagnosticsProperty('focusNode', focusNode)); properties.add(DiagnosticsProperty('obscureText', obscureText, defaultValue: false)); properties.add(DiagnosticsProperty('readOnly', readOnly, defaultValue: false)); - properties.add(DiagnosticsProperty('autocorrect', autocorrect, defaultValue: true)); + properties.add(DiagnosticsProperty('autocorrect', autocorrect, defaultValue: null)); properties.add( EnumProperty( 'smartDashesType', diff --git a/packages/flutter/test/widgets/editable_text_test.dart b/packages/flutter/test/widgets/editable_text_test.dart index 512b33c3592..05bbcf37071 100644 --- a/packages/flutter/test/widgets/editable_text_test.dart +++ b/packages/flutter/test/widgets/editable_text_test.dart @@ -3803,6 +3803,87 @@ void main() { }, ); + testWidgets( + 'iOS autocorrect value is inferred from AutofillHints - username', + (WidgetTester tester) async { + await tester.pumpWidget( + MediaQuery( + data: const MediaQueryData(), + child: Directionality( + textDirection: TextDirection.ltr, + child: EditableText( + controller: controller, + backgroundCursorColor: Colors.grey, + focusNode: focusNode, + style: textStyle, + cursorColor: cursorColor, + autofillHints: const [AutofillHints.username], + ), + ), + ), + ); + + final EditableText editableText = tester.firstWidget(find.byType(EditableText)); + expect(editableText.autocorrect, isFalse); + }, + variant: const TargetPlatformVariant({TargetPlatform.iOS}), + skip: kIsWeb, // [intended] + ); + + testWidgets( + 'iOS autocorrect value is inferred from AutofillHints - password', + (WidgetTester tester) async { + await tester.pumpWidget( + MediaQuery( + data: const MediaQueryData(), + child: Directionality( + textDirection: TextDirection.ltr, + child: EditableText( + controller: controller, + backgroundCursorColor: Colors.grey, + focusNode: focusNode, + style: textStyle, + cursorColor: cursorColor, + autofillHints: const [AutofillHints.password], + ), + ), + ), + ); + + final EditableText editableText = tester.firstWidget(find.byType(EditableText)); + expect(editableText.autocorrect, isFalse); + }, + variant: const TargetPlatformVariant({TargetPlatform.iOS}), + skip: kIsWeb, // [intended] + ); + + testWidgets( + 'iOS autocorrect value is inferred from AutofillHints - newPassword', + (WidgetTester tester) async { + await tester.pumpWidget( + MediaQuery( + data: const MediaQueryData(), + child: Directionality( + textDirection: TextDirection.ltr, + child: EditableText( + controller: controller, + backgroundCursorColor: Colors.grey, + focusNode: focusNode, + style: textStyle, + cursorColor: cursorColor, + autofillHints: const [AutofillHints.newPassword], + ), + ), + ), + ); + + final EditableText editableText = tester.firstWidget(find.byType(EditableText)); + expect(editableText.autocorrect, isFalse); + }, + variant: const TargetPlatformVariant({TargetPlatform.iOS}), + skip: kIsWeb, // [intended] + ); + testWidgets('Changing controller updates EditableText', (WidgetTester tester) async { final TextEditingController controller1 = TextEditingController(text: 'Wibble'); addTearDown(controller1.dispose);