mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
[iOS] Infer autocorrect value from autofillHints (#165637)
fix https://github.com/flutter/flutter/issues/134723 If autofillHints contains password-related hints, set autocorrect to `false`. https://github.com/user-attachments/assets/aeb95f74-cabc-40ff-8829-c49bce466a6d I checked the behaviour in UIKit and it is necessary to set correction and spell check to `No` if username or password is set to "Content Type". The following video shows the combination of `TextField` and option in UIKit. https://github.com/user-attachments/assets/6c442a87-042a-4b34-b88f-7a438fefdbb3 code: https://github.com/koji-1009/pm_behavior_test/tree/native ## Pre-launch Checklist - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [x] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [x] I signed the [CLA]. - [x] I listed at least one issue that this PR fixes in the description above. - [x] I updated/added relevant documentation (doc comments with `///`). - [x] I added new tests to check the change I am making, or this PR is [test-exempt]. - [x] I followed the [breaking change policy] and added [Data Driven Fixes] where supported. - [x] All existing and new tests are passing. If you need help, consider asking for advice on the #hackers-new channel on [Discord]. <!-- Links --> [Contributor Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview [Tree Hygiene]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md [test-exempt]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests [Flutter Style Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md [Features we expect every widget to implement]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement [CLA]: https://cla.developers.google.com/ [flutter/tests]: https://github.com/flutter/tests [breaking change policy]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes [Discord]: https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md [Data Driven Fixes]: https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md --------- Co-authored-by: Loïc Sharma <737941+loic-sharma@users.noreply.github.com>
This commit is contained in:
parent
2a1b863663
commit
21018df029
@ -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.
|
||||
|
||||
@ -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<String>('obscuringCharacter', obscuringCharacter, defaultValue: '•'),
|
||||
);
|
||||
properties.add(DiagnosticsProperty<bool>('obscureText', obscureText, defaultValue: false));
|
||||
properties.add(DiagnosticsProperty<bool>('autocorrect', autocorrect, defaultValue: true));
|
||||
properties.add(DiagnosticsProperty<bool>('autocorrect', autocorrect, defaultValue: null));
|
||||
properties.add(
|
||||
EnumProperty<SmartDashesType>(
|
||||
'smartDashesType',
|
||||
|
||||
@ -370,7 +370,9 @@ abstract class SearchDelegate<T> {
|
||||
/// 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}
|
||||
|
||||
@ -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<String>('obscuringCharacter', obscuringCharacter, defaultValue: '•'),
|
||||
);
|
||||
properties.add(DiagnosticsProperty<bool>('obscureText', obscureText, defaultValue: false));
|
||||
properties.add(DiagnosticsProperty<bool>('autocorrect', autocorrect, defaultValue: true));
|
||||
properties.add(DiagnosticsProperty<bool>('autocorrect', autocorrect, defaultValue: null));
|
||||
properties.add(
|
||||
EnumProperty<SmartDashesType>(
|
||||
'smartDashesType',
|
||||
@ -1695,6 +1695,7 @@ class _TextFieldState extends State<TextField>
|
||||
dragStartBehavior: widget.dragStartBehavior,
|
||||
scrollController: widget.scrollController,
|
||||
scrollPhysics: widget.scrollPhysics,
|
||||
autofillHints: widget.autofillHints,
|
||||
autofillClient: this,
|
||||
autocorrectionTextRectColor: autocorrectionTextRectColor,
|
||||
clipBehavior: widget.clipBehavior,
|
||||
|
||||
@ -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<String>? 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<String>? autofillHints,
|
||||
@ -2260,7 +2293,7 @@ class EditableText extends StatefulWidget {
|
||||
properties.add(DiagnosticsProperty<FocusNode>('focusNode', focusNode));
|
||||
properties.add(DiagnosticsProperty<bool>('obscureText', obscureText, defaultValue: false));
|
||||
properties.add(DiagnosticsProperty<bool>('readOnly', readOnly, defaultValue: false));
|
||||
properties.add(DiagnosticsProperty<bool>('autocorrect', autocorrect, defaultValue: true));
|
||||
properties.add(DiagnosticsProperty<bool>('autocorrect', autocorrect, defaultValue: null));
|
||||
properties.add(
|
||||
EnumProperty<SmartDashesType>(
|
||||
'smartDashesType',
|
||||
|
||||
@ -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 <String>[AutofillHints.username],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
final EditableText editableText = tester.firstWidget(find.byType(EditableText));
|
||||
expect(editableText.autocorrect, isFalse);
|
||||
},
|
||||
variant: const TargetPlatformVariant(<TargetPlatform>{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 <String>[AutofillHints.password],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
final EditableText editableText = tester.firstWidget(find.byType(EditableText));
|
||||
expect(editableText.autocorrect, isFalse);
|
||||
},
|
||||
variant: const TargetPlatformVariant(<TargetPlatform>{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 <String>[AutofillHints.newPassword],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
final EditableText editableText = tester.firstWidget(find.byType(EditableText));
|
||||
expect(editableText.autocorrect, isFalse);
|
||||
},
|
||||
variant: const TargetPlatformVariant(<TargetPlatform>{TargetPlatform.iOS}),
|
||||
skip: kIsWeb, // [intended]
|
||||
);
|
||||
|
||||
testWidgets('Changing controller updates EditableText', (WidgetTester tester) async {
|
||||
final TextEditingController controller1 = TextEditingController(text: 'Wibble');
|
||||
addTearDown(controller1.dispose);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user