Unverified Commit c5288c73 authored by Stanislav Baranov's avatar Stanislav Baranov Committed by GitHub

Support for decimal and signed numeric keyboard (#15846)

* Support for decimal and signed numeric keyboard

* Comments

* Rebase.

* Roll engine to dd6f46c485192f4506035088c9065b9f5dbba9ab
parent 0eec5ad0
3e877d371a359097d7af28aa6e85026fa2318baa dd6f46c485192f4506035088c9065b9f5dbba9ab
...@@ -270,7 +270,7 @@ class TextField extends StatefulWidget { ...@@ -270,7 +270,7 @@ class TextField extends StatefulWidget {
properties.add(new DiagnosticsProperty<TextEditingController>('controller', controller, defaultValue: null)); properties.add(new DiagnosticsProperty<TextEditingController>('controller', controller, defaultValue: null));
properties.add(new DiagnosticsProperty<FocusNode>('focusNode', focusNode, defaultValue: null)); properties.add(new DiagnosticsProperty<FocusNode>('focusNode', focusNode, defaultValue: null));
properties.add(new DiagnosticsProperty<InputDecoration>('decoration', decoration)); properties.add(new DiagnosticsProperty<InputDecoration>('decoration', decoration));
properties.add(new EnumProperty<TextInputType>('keyboardType', keyboardType, defaultValue: TextInputType.text)); properties.add(new DiagnosticsProperty<TextInputType>('keyboardType', keyboardType, defaultValue: TextInputType.text));
properties.add(new DiagnosticsProperty<TextStyle>('style', style, defaultValue: null)); properties.add(new DiagnosticsProperty<TextStyle>('style', style, defaultValue: null));
properties.add(new DiagnosticsProperty<bool>('autofocus', autofocus, defaultValue: false)); properties.add(new DiagnosticsProperty<bool>('autofocus', autofocus, defaultValue: false));
properties.add(new DiagnosticsProperty<bool>('obscureText', obscureText, defaultValue: false)); properties.add(new DiagnosticsProperty<bool>('obscureText', obscureText, defaultValue: false));
......
...@@ -16,29 +16,60 @@ export 'dart:ui' show TextAffinity; ...@@ -16,29 +16,60 @@ export 'dart:ui' show TextAffinity;
/// The type of information for which to optimize the text input control. /// The type of information for which to optimize the text input control.
/// ///
/// On Android, behavior may vary across device and keyboard provider. /// On Android, behavior may vary across device and keyboard provider.
enum TextInputType { ///
/// This class stays as close to [Enum] interface as possible, and allows
/// for additional flags for some input types. For example, numeric input
/// can specify whether it supports decimal numbers and/or signed numbers.
class TextInputType {
const TextInputType._(this.index) : signed = null, decimal = null;
/// Optimize for textual information.
///
/// Requests a numeric keyboard with additional settings.
/// The [signed] and [decimal] parameters are optional.
const TextInputType.numberWithOptions({
this.signed: false,
this.decimal: false,
}) : index = 2;
/// Enum value index, corresponds to one of the [values].
final int index;
/// The number is signed, allowing a positive or negative sign at the start.
///
/// This flag is only used for the [number] input type, otherwise `null`.
/// Use `const TextInputType.numberWithOptions(signed: true)` to set this.
final bool signed;
/// The number is decimal, allowing a decimal point to provide fractional.
///
/// This flag is only used for the [number] input type, otherwise `null`.
/// Use `const TextInputType.numberWithOptions(decimal: true)` to set this.
final bool decimal;
/// Optimize for textual information. /// Optimize for textual information.
/// ///
/// Requests the default platform keyboard. /// Requests the default platform keyboard.
text, static const TextInputType text = const TextInputType._(0);
/// Optimize for multi-line textual information. /// Optimize for multi-line textual information.
/// ///
/// Requests the default platform keyboard, but accepts newlines when the /// Requests the default platform keyboard, but accepts newlines when the
/// enter key is pressed. This is the input type used for all multi-line text /// enter key is pressed. This is the input type used for all multi-line text
/// fields. /// fields.
multiline, static const TextInputType multiline = const TextInputType._(1);
/// Optimize for numerical information. /// Optimize for numerical information.
/// ///
/// Requests a keyboard with ready access to the decimal point and number /// Requests a default keyboard with ready access to the number keys.
/// keys. /// Additional options, such as decimal point and/or positive/negative
number, /// signs, can be requested using [new TextInputType.numberWithOptions].
static const TextInputType number = const TextInputType.numberWithOptions();
/// Optimize for telephone numbers. /// Optimize for telephone numbers.
/// ///
/// Requests a keyboard with ready access to the number keys, "*", and "#". /// Requests a keyboard with ready access to the number keys, "*", and "#".
phone, static const TextInputType phone = const TextInputType._(3);
/// Optimize for date and time information. /// Optimize for date and time information.
/// ///
...@@ -46,17 +77,60 @@ enum TextInputType { ...@@ -46,17 +77,60 @@ enum TextInputType {
/// ///
/// On Android, requests a keyboard with ready access to the number keys, /// On Android, requests a keyboard with ready access to the number keys,
/// ":", and "-". /// ":", and "-".
datetime, static const TextInputType datetime = const TextInputType._(4);
/// Optimize for email addresses. /// Optimize for email addresses.
/// ///
/// Requests a keyboard with ready access to the "@" and "." keys. /// Requests a keyboard with ready access to the "@" and "." keys.
emailAddress, static const TextInputType emailAddress = const TextInputType._(5);
/// Optimize for URLs. /// Optimize for URLs.
/// ///
/// Requests a keyboard with ready access to the "/" and "." keys. /// Requests a keyboard with ready access to the "/" and "." keys.
url, static const TextInputType url = const TextInputType._(6);
/// All possible enum values.
static const List<TextInputType> values = const <TextInputType>[
text, multiline, number, phone, datetime, emailAddress, url,
];
// Corresponding string name for each the [values].
static const List<String> _names = const <String>[
'text', 'multiline', 'number', 'phone', 'datetime', 'emailAddress', 'url',
];
// Enum value name, this is what enum.toString() would normally return.
String get _name => 'TextInputType.${_names[index]}';
/// Returns a representation of this object as a JSON object.
Map<String, dynamic> toJSON() {
return <String, dynamic>{
'name': _name,
'signed': signed,
'decimal': decimal,
};
}
@override
String toString() {
return '$runtimeType('
'name: $_name, '
'signed: $signed, '
'decimal: $decimal)';
}
@override
bool operator ==(dynamic other) {
if (other is! TextInputType)
return false;
final TextInputType typedOther = other;
return typedOther.index == index
&& typedOther.signed == signed
&& typedOther.decimal == decimal;
}
@override
int get hashCode => hashValues(index, signed, decimal);
} }
/// An action the user has requested the text input control to perform. /// An action the user has requested the text input control to perform.
...@@ -113,7 +187,7 @@ class TextInputConfiguration { ...@@ -113,7 +187,7 @@ class TextInputConfiguration {
/// Returns a representation of this object as a JSON object. /// Returns a representation of this object as a JSON object.
Map<String, dynamic> toJSON() { Map<String, dynamic> toJSON() {
return <String, dynamic>{ return <String, dynamic>{
'inputType': inputType.toString(), 'inputType': inputType.toJSON(),
'obscureText': obscureText, 'obscureText': obscureText,
'autocorrect': autocorrect, 'autocorrect': autocorrect,
'actionLabel': actionLabel, 'actionLabel': actionLabel,
......
...@@ -303,7 +303,7 @@ class EditableText extends StatefulWidget { ...@@ -303,7 +303,7 @@ class EditableText extends StatefulWidget {
properties.add(new DoubleProperty('textScaleFactor', textScaleFactor, defaultValue: null)); properties.add(new DoubleProperty('textScaleFactor', textScaleFactor, defaultValue: null));
properties.add(new IntProperty('maxLines', maxLines, defaultValue: 1)); properties.add(new IntProperty('maxLines', maxLines, defaultValue: 1));
properties.add(new DiagnosticsProperty<bool>('autofocus', autofocus, defaultValue: false)); properties.add(new DiagnosticsProperty<bool>('autofocus', autofocus, defaultValue: false));
properties.add(new EnumProperty<TextInputType>('keyboardType', keyboardType, defaultValue: null)); properties.add(new DiagnosticsProperty<TextInputType>('keyboardType', keyboardType, defaultValue: null));
} }
} }
......
...@@ -15,18 +15,69 @@ void main() { ...@@ -15,18 +15,69 @@ void main() {
expect(configuration.actionLabel, null); expect(configuration.actionLabel, null);
}); });
test('serializes to JSON', () async { test('text serializes to JSON', () async {
const TextInputConfiguration configuration = const TextInputConfiguration( const TextInputConfiguration configuration = const TextInputConfiguration(
inputType: TextInputType.number, inputType: TextInputType.text,
obscureText: true, obscureText: true,
autocorrect: false, autocorrect: false,
actionLabel: 'xyzzy' actionLabel: 'xyzzy',
); );
final Map<String, dynamic> json = configuration.toJSON(); final Map<String, dynamic> json = configuration.toJSON();
expect(json['inputType'], 'TextInputType.number'); expect(json['inputType'], <String, dynamic>{
'name': 'TextInputType.text', 'signed': null, 'decimal': null
});
expect(json['obscureText'], true); expect(json['obscureText'], true);
expect(json['autocorrect'], false); expect(json['autocorrect'], false);
expect(json['actionLabel'], 'xyzzy'); expect(json['actionLabel'], 'xyzzy');
}); });
test('number serializes to JSON', () async {
const TextInputConfiguration configuration = const TextInputConfiguration(
inputType: const TextInputType.numberWithOptions(decimal: true),
obscureText: true,
autocorrect: false,
actionLabel: 'xyzzy',
);
final Map<String, dynamic> json = configuration.toJSON();
expect(json['inputType'], <String, dynamic>{
'name': 'TextInputType.number', 'signed': false, 'decimal': true
});
expect(json['obscureText'], true);
expect(json['autocorrect'], false);
expect(json['actionLabel'], 'xyzzy');
});
test('basic structure', () async {
const TextInputType text = TextInputType.text;
const TextInputType number = TextInputType.number;
const TextInputType number2 = const TextInputType.numberWithOptions();
const TextInputType signed = const TextInputType.numberWithOptions(signed: true);
const TextInputType signed2 = const TextInputType.numberWithOptions(signed: true);
const TextInputType decimal = const TextInputType.numberWithOptions(decimal: true);
const TextInputType signedDecimal =
const TextInputType.numberWithOptions(signed: true, decimal: true);
expect(text.toString(), 'TextInputType(name: TextInputType.text, signed: null, decimal: null)');
expect(number.toString(), 'TextInputType(name: TextInputType.number, signed: false, decimal: false)');
expect(signed.toString(), 'TextInputType(name: TextInputType.number, signed: true, decimal: false)');
expect(decimal.toString(), 'TextInputType(name: TextInputType.number, signed: false, decimal: true)');
expect(signedDecimal.toString(), 'TextInputType(name: TextInputType.number, signed: true, decimal: true)');
expect(text == number, false);
expect(number == number2, true);
expect(number == signed, false);
expect(signed == signed2, true);
expect(signed == decimal, false);
expect(signed == signedDecimal, false);
expect(decimal == signedDecimal, false);
expect(text.hashCode == number.hashCode, false);
expect(number.hashCode == number2.hashCode, true);
expect(number.hashCode == signed.hashCode, false);
expect(signed.hashCode == signed2.hashCode, true);
expect(signed.hashCode == decimal.hashCode, false);
expect(signed.hashCode == signedDecimal.hashCode, false);
expect(decimal.hashCode == signedDecimal.hashCode, false);
});
}); });
} }
...@@ -62,8 +62,7 @@ void main() { ...@@ -62,8 +62,7 @@ void main() {
tester.firstWidget(find.byType(EditableText)); tester.firstWidget(find.byType(EditableText));
expect(editableText.maxLines, equals(1)); expect(editableText.maxLines, equals(1));
expect(tester.testTextInput.editingState['text'], equals('test')); expect(tester.testTextInput.editingState['text'], equals('test'));
expect(tester.testTextInput.setClientArgs['inputType'], expect(tester.testTextInput.setClientArgs['inputType']['name'], equals('TextInputType.text'));
equals('TextInputType.text'));
expect(tester.testTextInput.setClientArgs['inputAction'], expect(tester.testTextInput.setClientArgs['inputAction'],
equals('TextInputAction.done')); equals('TextInputAction.done'));
}); });
...@@ -88,7 +87,7 @@ void main() { ...@@ -88,7 +87,7 @@ void main() {
controller.text = 'test'; controller.text = 'test';
await tester.idle(); await tester.idle();
expect(tester.testTextInput.editingState['text'], equals('test')); expect(tester.testTextInput.editingState['text'], equals('test'));
expect(tester.testTextInput.setClientArgs['inputType'], equals('TextInputType.multiline')); expect(tester.testTextInput.setClientArgs['inputType']['name'], equals('TextInputType.multiline'));
expect(tester.testTextInput.setClientArgs['inputAction'], equals('TextInputAction.newline')); expect(tester.testTextInput.setClientArgs['inputAction'], equals('TextInputAction.newline'));
}); });
...@@ -113,7 +112,7 @@ void main() { ...@@ -113,7 +112,7 @@ void main() {
controller.text = 'test'; controller.text = 'test';
await tester.idle(); await tester.idle();
expect(tester.testTextInput.editingState['text'], equals('test')); expect(tester.testTextInput.editingState['text'], equals('test'));
expect(tester.testTextInput.setClientArgs['inputType'], equals('TextInputType.phone')); expect(tester.testTextInput.setClientArgs['inputType']['name'], equals('TextInputType.phone'));
expect(tester.testTextInput.setClientArgs['inputAction'], equals('TextInputAction.done')); expect(tester.testTextInput.setClientArgs['inputAction'], equals('TextInputAction.done'));
}); });
...@@ -137,7 +136,7 @@ void main() { ...@@ -137,7 +136,7 @@ void main() {
controller.text = 'test'; controller.text = 'test';
await tester.idle(); await tester.idle();
expect(tester.testTextInput.editingState['text'], equals('test')); expect(tester.testTextInput.editingState['text'], equals('test'));
expect(tester.testTextInput.setClientArgs['inputType'], equals('TextInputType.multiline')); expect(tester.testTextInput.setClientArgs['inputType']['name'], equals('TextInputType.multiline'));
expect(tester.testTextInput.setClientArgs['inputAction'], equals('TextInputAction.newline')); expect(tester.testTextInput.setClientArgs['inputAction'], equals('TextInputAction.newline'));
}); });
...@@ -161,7 +160,7 @@ void main() { ...@@ -161,7 +160,7 @@ void main() {
controller.text = 'test'; controller.text = 'test';
await tester.idle(); await tester.idle();
expect(tester.testTextInput.editingState['text'], equals('test')); expect(tester.testTextInput.editingState['text'], equals('test'));
expect(tester.testTextInput.setClientArgs['inputType'], equals('TextInputType.text')); expect(tester.testTextInput.setClientArgs['inputType']['name'], equals('TextInputType.text'));
expect(tester.testTextInput.setClientArgs['inputAction'], equals('TextInputAction.done')); expect(tester.testTextInput.setClientArgs['inputAction'], equals('TextInputAction.done'));
}); });
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment