Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Sign in
Toggle navigation
F
Front-End
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
abdullh.alsoleman
Front-End
Commits
b64d652f
Unverified
Commit
b64d652f
authored
Jun 08, 2020
by
LongCatIsLooong
Committed by
GitHub
Jun 08, 2020
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add 2 new keyboard types and infer keyboardType from autofill hints (#56641)
parent
70a88c3b
Changes
6
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
321 additions
and
13 deletions
+321
-13
text_field.dart
packages/flutter/lib/src/cupertino/text_field.dart
+1
-0
text_field.dart
packages/flutter/lib/src/material/text_field.dart
+1
-0
autofill.dart
packages/flutter/lib/src/services/autofill.dart
+12
-6
text_input.dart
packages/flutter/lib/src/services/text_input.dart
+21
-2
editable_text.dart
packages/flutter/lib/src/widgets/editable_text.dart
+158
-5
editable_text_test.dart
packages/flutter/test/widgets/editable_text_test.dart
+128
-0
No files found.
packages/flutter/lib/src/cupertino/text_field.dart
View file @
b64d652f
...
...
@@ -591,6 +591,7 @@ class CupertinoTextField extends StatefulWidget {
final
GestureTapCallback
onTap
;
/// {@macro flutter.widgets.editableText.autofillHints}
/// {@macro flutter.services.autofill.autofillHints}
final
Iterable
<
String
>
autofillHints
;
@override
...
...
packages/flutter/lib/src/material/text_field.dart
View file @
b64d652f
...
...
@@ -728,6 +728,7 @@ class TextField extends StatefulWidget {
final
ScrollController
scrollController
;
/// {@macro flutter.widgets.editableText.autofillHints}
/// {@macro flutter.services.autofill.autofillHints}
final
Iterable
<
String
>
autofillHints
;
/// Indicates whether [debugCheckHasMaterialLocalizations] can be called
...
...
packages/flutter/lib/src/services/autofill.dart
View file @
b64d652f
...
...
@@ -7,9 +7,9 @@ import 'text_input.dart';
/// A collection of commonly used autofill hint strings on different platforms.
///
/// Each hint
may not be supported on every platform, and may get translated to
/// d
ifferent strings on different platforms. Please refer to their documentation
///
for what each value corresponds to on different platforms
.
/// Each hint
is pre-defined on at least one supported platform. See their
/// d
ocumentation for their availability on each platform, and the platform
///
values each autofill hint corresponds to
.
class
AutofillHints
{
AutofillHints
.
_
();
...
...
@@ -350,7 +350,7 @@ class AutofillHints {
/// * Otherwise, the hint string will be used as-is.
static
const
String
nickname
=
'nickname'
;
/// The input field expects a
single-factor SMS login
code.
/// The input field expects a
SMS one-time
code.
///
/// This hint will be translated to the below values on different platforms:
///
...
...
@@ -649,9 +649,9 @@ class AutofillConfiguration {
/// {@template flutter.services.autofill.autofillHints}
/// For the best results, hint strings need to be understood by the platform's
/// autofill service. The common values of hint strings can be found in
/// [AutofillHints], as well as the
platforms that understand each of them
.
/// [AutofillHints], as well as the
ir availability on different platforms
.
///
/// If an autofillable input field needs to use a custom hint that translate to
/// If an autofillable input field needs to use a custom hint that translate
s
to
/// different strings on different platforms, the easiest way to achieve that
/// is to return different hint strings based on the value of
/// [defaultTargetPlatform].
...
...
@@ -668,6 +668,12 @@ class AutofillConfiguration {
/// * On web, only the first hint is accounted for and will be translated to
/// an "autocomplete" string.
///
/// Providing an autofill hint that is predefined on the platform does not
/// automatically grant the input field eligibility for autofill. Ultimately,
/// it comes down to the autofill service currently in charge to determine
/// whether an input field is suitable for autofill and what the autofill
/// candidates are.
///
/// See also:
///
/// * [AutofillHints], a list of autofill hint strings that is predefined on at
...
...
packages/flutter/lib/src/services/text_input.dart
View file @
b64d652f
...
...
@@ -157,14 +157,33 @@ class TextInputType {
/// Requests a keyboard with ready access to both letters and numbers.
static
const
TextInputType
visiblePassword
=
TextInputType
.
_
(
7
);
/// Optimized for a person's name.
///
/// On iOS, requests the
/// [UIKeyboardType.namePhonePad](https://developer.apple.com/documentation/uikit/uikeyboardtype/namephonepad)
/// keyboard, a keyboard optimized for entering a person’s name or phone number.
/// Does not support auto-capitalization.
///
/// On Android, requests a keyboard optimized for
/// [TYPE_TEXT_VARIATION_PERSON_NAME](https://developer.android.com/reference/android/text/InputType#TYPE_TEXT_VARIATION_PERSON_NAME).
static
const
TextInputType
name
=
TextInputType
.
_
(
8
);
/// Optimized for postal mailing addresses.
///
/// On iOS, requests the default keyboard.
///
/// On Android, requests a keyboard optimized for
/// [TYPE_TEXT_VARIATION_POSTAL_ADDRESS](https://developer.android.com/reference/android/text/InputType#TYPE_TEXT_VARIATION_POSTAL_ADDRESS).
static
const
TextInputType
streetAddress
=
TextInputType
.
_
(
9
);
/// All possible enum values.
static
const
List
<
TextInputType
>
values
=
<
TextInputType
>[
text
,
multiline
,
number
,
phone
,
datetime
,
emailAddress
,
url
,
visiblePassword
,
text
,
multiline
,
number
,
phone
,
datetime
,
emailAddress
,
url
,
visiblePassword
,
name
,
streetAddress
,
];
// Corresponding string name for each of the [values].
static
const
List
<
String
>
_names
=
<
String
>[
'text'
,
'multiline'
,
'number'
,
'phone'
,
'datetime'
,
'emailAddress'
,
'url'
,
'visiblePassword'
,
'text'
,
'multiline'
,
'number'
,
'phone'
,
'datetime'
,
'emailAddress'
,
'url'
,
'visiblePassword'
,
'name'
,
'address'
,
];
// Enum value name, this is what enum.toString() would normally return.
...
...
packages/flutter/lib/src/widgets/editable_text.dart
View file @
b64d652f
...
...
@@ -338,9 +338,10 @@ class EditableText extends StatefulWidget {
/// the number of lines. By default, it is one, meaning this is a single-line
/// text field. [maxLines] must be null or greater than zero.
///
/// If [keyboardType] is not set or is null, it will default to
/// [TextInputType.text] unless [maxLines] is greater than one, when it will
/// default to [TextInputType.multiline].
/// If [keyboardType] is not set or is null, its value will be inferred from
/// [autofillHints], if [autofillHints] is not empty. Otherwise it defaults to
/// [TextInputType.text] if [maxLines] is exactly one, and
/// [TextInputType.multiline] if [maxLines] is null or greater than one.
///
/// The text cursor is not shown if [showCursor] is false or if [showCursor]
/// is null (the default) and [readOnly] is true.
...
...
@@ -452,7 +453,7 @@ class EditableText extends StatefulWidget {
assert
(
dragStartBehavior
!=
null
),
assert
(
toolbarOptions
!=
null
),
_strutStyle
=
strutStyle
,
keyboardType
=
keyboardType
??
(
maxLines
==
1
?
TextInputType
.
text
:
TextInputType
.
multiline
),
keyboardType
=
keyboardType
??
_inferKeyboardType
(
autofillHints:
autofillHints
,
maxLines:
maxLines
),
inputFormatters
=
maxLines
==
1
?
<
TextInputFormatter
>[
BlacklistingTextInputFormatter
.
singleLineFormatter
,
...
...
@@ -1120,10 +1121,162 @@ class EditableText extends StatefulWidget {
/// The minimum platform SDK version that supports Autofill is API level 26
/// for Android, and iOS 10.0 for iOS.
///
/// {@macro flutter.services.autofill.autofillHints}
/// ### iOS-specific Concerns:
///
/// To provide the best user experience and ensure your app fully supports
/// password autofill on iOS, follow these steps:
///
/// * Set up your iOS app's
/// [associated domains](https://developer.apple.com/documentation/safariservices/supporting_associated_domains_in_your_app).
/// * Some autofill hints only work with specific [keyboardType]s. For example,
/// [AutofillHints.name] requires [TextInputType.name] and [AutofillHints.email]
/// works only with [TextInputType.email]. Make sure the input field has a
/// compatible [keyboardType]. Empirically, [TextInputType.name] works well
/// with many autofill hints that are predefined on iOS.
/// {@endtemplate}
/// {@macro flutter.services.autofill.autofillHints}
final
Iterable
<
String
>
autofillHints
;
// Infer the keyboard type of an `EditableText` if it's not specified.
static
TextInputType
_inferKeyboardType
({
@required
Iterable
<
String
>
autofillHints
,
@required
int
maxLines
,
})
{
if
(
autofillHints
?.
isEmpty
??
true
)
{
return
maxLines
==
1
?
TextInputType
.
text
:
TextInputType
.
multiline
;
}
TextInputType
returnValue
;
final
String
effectiveHint
=
autofillHints
.
first
;
// On iOS oftentimes specifying a text content type is not enough to qualify
// the input field for autofill. The keyboard type also needs to be compatible
// with the content type. To get autofill to work by default on EditableText,
// the keyboard type inference on iOS is done differently from other platforms.
//
// The entries with "autofill not working" comments are the iOS text content
// types that should work with the specified keyboard type but won't trigger
// (even within a native app). Tested on iOS 13.5.
if
(!
kIsWeb
)
{
switch
(
defaultTargetPlatform
)
{
case
TargetPlatform
.
iOS
:
case
TargetPlatform
.
macOS
:
const
Map
<
String
,
TextInputType
>
iOSKeyboardType
=
<
String
,
TextInputType
>
{
AutofillHints
.
addressCity
:
TextInputType
.
name
,
AutofillHints
.
addressCityAndState
:
TextInputType
.
name
,
// Autofill not working.
AutofillHints
.
addressState
:
TextInputType
.
name
,
AutofillHints
.
countryName
:
TextInputType
.
name
,
AutofillHints
.
creditCardNumber
:
TextInputType
.
number
,
// Couldn't test.
AutofillHints
.
email
:
TextInputType
.
emailAddress
,
AutofillHints
.
familyName
:
TextInputType
.
name
,
AutofillHints
.
fullStreetAddress
:
TextInputType
.
name
,
AutofillHints
.
givenName
:
TextInputType
.
name
,
AutofillHints
.
jobTitle
:
TextInputType
.
name
,
// Autofill not working.
AutofillHints
.
location
:
TextInputType
.
name
,
// Autofill not working.
AutofillHints
.
middleName
:
TextInputType
.
name
,
// Autofill not working.
AutofillHints
.
name
:
TextInputType
.
name
,
AutofillHints
.
namePrefix
:
TextInputType
.
name
,
// Autofill not working.
AutofillHints
.
nameSuffix
:
TextInputType
.
name
,
// Autofill not working.
AutofillHints
.
newPassword
:
TextInputType
.
text
,
AutofillHints
.
newUsername
:
TextInputType
.
text
,
AutofillHints
.
nickname
:
TextInputType
.
name
,
// Autofill not working.
AutofillHints
.
oneTimeCode
:
TextInputType
.
number
,
AutofillHints
.
organizationName
:
TextInputType
.
text
,
// Autofill not working.
AutofillHints
.
password
:
TextInputType
.
text
,
AutofillHints
.
postalCode
:
TextInputType
.
name
,
AutofillHints
.
streetAddressLine1
:
TextInputType
.
name
,
AutofillHints
.
streetAddressLine2
:
TextInputType
.
name
,
// Autofill not working.
AutofillHints
.
sublocality
:
TextInputType
.
name
,
// Autofill not working.
AutofillHints
.
telephoneNumber
:
TextInputType
.
name
,
AutofillHints
.
url
:
TextInputType
.
url
,
// Autofill not working.
AutofillHints
.
username
:
TextInputType
.
text
,
};
returnValue
=
iOSKeyboardType
[
effectiveHint
];
break
;
case
TargetPlatform
.
android
:
case
TargetPlatform
.
fuchsia
:
case
TargetPlatform
.
linux
:
case
TargetPlatform
.
windows
:
break
;
}
}
if
(
returnValue
!=
null
||
maxLines
!=
1
)
return
returnValue
??
TextInputType
.
multiline
;
const
Map
<
String
,
TextInputType
>
inferKeyboardType
=
<
String
,
TextInputType
>
{
AutofillHints
.
addressCity
:
TextInputType
.
streetAddress
,
AutofillHints
.
addressCityAndState
:
TextInputType
.
streetAddress
,
AutofillHints
.
addressState
:
TextInputType
.
streetAddress
,
AutofillHints
.
birthday
:
TextInputType
.
datetime
,
AutofillHints
.
birthdayDay
:
TextInputType
.
datetime
,
AutofillHints
.
birthdayMonth
:
TextInputType
.
datetime
,
AutofillHints
.
birthdayYear
:
TextInputType
.
datetime
,
AutofillHints
.
countryCode
:
TextInputType
.
number
,
AutofillHints
.
countryName
:
TextInputType
.
text
,
AutofillHints
.
creditCardExpirationDate
:
TextInputType
.
datetime
,
AutofillHints
.
creditCardExpirationDay
:
TextInputType
.
datetime
,
AutofillHints
.
creditCardExpirationMonth
:
TextInputType
.
datetime
,
AutofillHints
.
creditCardExpirationYear
:
TextInputType
.
datetime
,
AutofillHints
.
creditCardFamilyName
:
TextInputType
.
name
,
AutofillHints
.
creditCardGivenName
:
TextInputType
.
name
,
AutofillHints
.
creditCardMiddleName
:
TextInputType
.
name
,
AutofillHints
.
creditCardName
:
TextInputType
.
name
,
AutofillHints
.
creditCardNumber
:
TextInputType
.
number
,
AutofillHints
.
creditCardSecurityCode
:
TextInputType
.
number
,
AutofillHints
.
creditCardType
:
TextInputType
.
text
,
AutofillHints
.
email
:
TextInputType
.
emailAddress
,
AutofillHints
.
familyName
:
TextInputType
.
name
,
AutofillHints
.
fullStreetAddress
:
TextInputType
.
streetAddress
,
AutofillHints
.
gender
:
TextInputType
.
text
,
AutofillHints
.
givenName
:
TextInputType
.
name
,
AutofillHints
.
impp
:
TextInputType
.
url
,
AutofillHints
.
jobTitle
:
TextInputType
.
text
,
AutofillHints
.
language
:
TextInputType
.
text
,
AutofillHints
.
location
:
TextInputType
.
streetAddress
,
AutofillHints
.
middleInitial
:
TextInputType
.
name
,
AutofillHints
.
middleName
:
TextInputType
.
name
,
AutofillHints
.
name
:
TextInputType
.
name
,
AutofillHints
.
namePrefix
:
TextInputType
.
name
,
AutofillHints
.
nameSuffix
:
TextInputType
.
name
,
AutofillHints
.
newPassword
:
TextInputType
.
text
,
AutofillHints
.
newUsername
:
TextInputType
.
text
,
AutofillHints
.
nickname
:
TextInputType
.
text
,
AutofillHints
.
oneTimeCode
:
TextInputType
.
text
,
AutofillHints
.
organizationName
:
TextInputType
.
text
,
AutofillHints
.
password
:
TextInputType
.
text
,
AutofillHints
.
photo
:
TextInputType
.
text
,
AutofillHints
.
postalAddress
:
TextInputType
.
streetAddress
,
AutofillHints
.
postalAddressExtended
:
TextInputType
.
streetAddress
,
AutofillHints
.
postalAddressExtendedPostalCode
:
TextInputType
.
number
,
AutofillHints
.
postalCode
:
TextInputType
.
number
,
AutofillHints
.
streetAddressLevel1
:
TextInputType
.
streetAddress
,
AutofillHints
.
streetAddressLevel2
:
TextInputType
.
streetAddress
,
AutofillHints
.
streetAddressLevel3
:
TextInputType
.
streetAddress
,
AutofillHints
.
streetAddressLevel4
:
TextInputType
.
streetAddress
,
AutofillHints
.
streetAddressLine1
:
TextInputType
.
streetAddress
,
AutofillHints
.
streetAddressLine2
:
TextInputType
.
streetAddress
,
AutofillHints
.
streetAddressLine3
:
TextInputType
.
streetAddress
,
AutofillHints
.
sublocality
:
TextInputType
.
streetAddress
,
AutofillHints
.
telephoneNumber
:
TextInputType
.
phone
,
AutofillHints
.
telephoneNumberAreaCode
:
TextInputType
.
phone
,
AutofillHints
.
telephoneNumberCountryCode
:
TextInputType
.
phone
,
AutofillHints
.
telephoneNumberDevice
:
TextInputType
.
phone
,
AutofillHints
.
telephoneNumberExtension
:
TextInputType
.
phone
,
AutofillHints
.
telephoneNumberLocal
:
TextInputType
.
phone
,
AutofillHints
.
telephoneNumberLocalPrefix
:
TextInputType
.
phone
,
AutofillHints
.
telephoneNumberLocalSuffix
:
TextInputType
.
phone
,
AutofillHints
.
telephoneNumberNational
:
TextInputType
.
phone
,
AutofillHints
.
transactionAmount
:
TextInputType
.
numberWithOptions
(
decimal:
true
),
AutofillHints
.
transactionCurrency
:
TextInputType
.
text
,
AutofillHints
.
url
:
TextInputType
.
url
,
AutofillHints
.
username
:
TextInputType
.
text
,
};
return
inferKeyboardType
[
effectiveHint
]
??
TextInputType
.
text
;
}
@override
EditableTextState
createState
()
=>
EditableTextState
();
...
...
packages/flutter/test/widgets/editable_text_test.dart
View file @
b64d652f
...
...
@@ -267,6 +267,134 @@ void main() {
);
});
group
(
'Infer keyboardType from autofillHints'
,
()
{
testWidgets
(
'infer keyboard types from autofillHints: ios'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
MediaQuery
(
data:
const
MediaQueryData
(
devicePixelRatio:
1.0
),
child:
Directionality
(
textDirection:
TextDirection
.
ltr
,
child:
FocusScope
(
node:
focusScopeNode
,
autofocus:
true
,
child:
EditableText
(
controller:
controller
,
backgroundCursorColor:
Colors
.
grey
,
focusNode:
focusNode
,
style:
textStyle
,
cursorColor:
cursorColor
,
autofillHints:
const
<
String
>[
AutofillHints
.
streetAddressLine1
],
),
),
),
),
);
await
tester
.
tap
(
find
.
byType
(
EditableText
));
await
tester
.
showKeyboard
(
find
.
byType
(
EditableText
));
controller
.
text
=
'test'
;
await
tester
.
idle
();
expect
(
tester
.
testTextInput
.
editingState
[
'text'
],
equals
(
'test'
));
expect
(
tester
.
testTextInput
.
setClientArgs
[
'inputType'
][
'name'
],
equals
(
'TextInputType.name'
));
},
variant:
const
TargetPlatformVariant
(<
TargetPlatform
>{
TargetPlatform
.
iOS
,
TargetPlatform
.
macOS
}));
testWidgets
(
'infer keyboard types from autofillHints: non-ios'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
MediaQuery
(
data:
const
MediaQueryData
(
devicePixelRatio:
1.0
),
child:
Directionality
(
textDirection:
TextDirection
.
ltr
,
child:
FocusScope
(
node:
focusScopeNode
,
autofocus:
true
,
child:
EditableText
(
controller:
controller
,
backgroundCursorColor:
Colors
.
grey
,
focusNode:
focusNode
,
style:
textStyle
,
cursorColor:
cursorColor
,
autofillHints:
const
<
String
>[
AutofillHints
.
streetAddressLine1
],
),
),
),
),
);
await
tester
.
tap
(
find
.
byType
(
EditableText
));
await
tester
.
showKeyboard
(
find
.
byType
(
EditableText
));
controller
.
text
=
'test'
;
await
tester
.
idle
();
expect
(
tester
.
testTextInput
.
editingState
[
'text'
],
equals
(
'test'
));
expect
(
tester
.
testTextInput
.
setClientArgs
[
'inputType'
][
'name'
],
equals
(
'TextInputType.address'
));
});
testWidgets
(
'inferred keyboard types can be overridden: ios'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
MediaQuery
(
data:
const
MediaQueryData
(
devicePixelRatio:
1.0
),
child:
Directionality
(
textDirection:
TextDirection
.
ltr
,
child:
FocusScope
(
node:
focusScopeNode
,
autofocus:
true
,
child:
EditableText
(
controller:
controller
,
backgroundCursorColor:
Colors
.
grey
,
focusNode:
focusNode
,
style:
textStyle
,
cursorColor:
cursorColor
,
keyboardType:
TextInputType
.
text
,
autofillHints:
const
<
String
>[
AutofillHints
.
streetAddressLine1
],
),
),
),
),
);
await
tester
.
tap
(
find
.
byType
(
EditableText
));
await
tester
.
showKeyboard
(
find
.
byType
(
EditableText
));
controller
.
text
=
'test'
;
await
tester
.
idle
();
expect
(
tester
.
testTextInput
.
editingState
[
'text'
],
equals
(
'test'
));
expect
(
tester
.
testTextInput
.
setClientArgs
[
'inputType'
][
'name'
],
equals
(
'TextInputType.text'
));
},
variant:
const
TargetPlatformVariant
(<
TargetPlatform
>{
TargetPlatform
.
iOS
,
TargetPlatform
.
macOS
}));
testWidgets
(
'inferred keyboard types can be overridden: non-ios'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
MediaQuery
(
data:
const
MediaQueryData
(
devicePixelRatio:
1.0
),
child:
Directionality
(
textDirection:
TextDirection
.
ltr
,
child:
FocusScope
(
node:
focusScopeNode
,
autofocus:
true
,
child:
EditableText
(
controller:
controller
,
backgroundCursorColor:
Colors
.
grey
,
focusNode:
focusNode
,
style:
textStyle
,
cursorColor:
cursorColor
,
keyboardType:
TextInputType
.
text
,
autofillHints:
const
<
String
>[
AutofillHints
.
streetAddressLine1
],
),
),
),
),
);
await
tester
.
tap
(
find
.
byType
(
EditableText
));
await
tester
.
showKeyboard
(
find
.
byType
(
EditableText
));
controller
.
text
=
'test'
;
await
tester
.
idle
();
expect
(
tester
.
testTextInput
.
editingState
[
'text'
],
equals
(
'test'
));
expect
(
tester
.
testTextInput
.
setClientArgs
[
'inputType'
][
'name'
],
equals
(
'TextInputType.text'
));
});
});
testWidgets
(
'multiline keyboard is requested when set explicitly'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
MediaQuery
(
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment