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 {
properties.add(new DiagnosticsProperty<TextEditingController>('controller', controller, defaultValue: null));
properties.add(new DiagnosticsProperty<FocusNode>('focusNode', focusNode, defaultValue: null));
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<bool>('autofocus', autofocus, defaultValue: false));
properties.add(new DiagnosticsProperty<bool>('obscureText', obscureText, defaultValue: false));
......
......@@ -16,29 +16,60 @@ export 'dart:ui' show TextAffinity;
/// The type of information for which to optimize the text input control.
///
/// 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.
///
/// Requests the default platform keyboard.
text,
static const TextInputType text = const TextInputType._(0);
/// Optimize for multi-line textual information.
///
/// 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
/// fields.
multiline,
static const TextInputType multiline = const TextInputType._(1);
/// Optimize for numerical information.
///
/// Requests a keyboard with ready access to the decimal point and number
/// keys.
number,
/// Requests a default keyboard with ready access to the number keys.
/// Additional options, such as decimal point and/or positive/negative
/// signs, can be requested using [new TextInputType.numberWithOptions].
static const TextInputType number = const TextInputType.numberWithOptions();
/// Optimize for telephone numbers.
///
/// 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.
///
......@@ -46,17 +77,60 @@ enum TextInputType {
///
/// On Android, requests a keyboard with ready access to the number keys,
/// ":", and "-".
datetime,
static const TextInputType datetime = const TextInputType._(4);
/// Optimize for email addresses.
///
/// Requests a keyboard with ready access to the "@" and "." keys.
emailAddress,
static const TextInputType emailAddress = const TextInputType._(5);
/// Optimize for URLs.
///
/// 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.
......@@ -113,7 +187,7 @@ class TextInputConfiguration {
/// Returns a representation of this object as a JSON object.
Map<String, dynamic> toJSON() {
return <String, dynamic>{
'inputType': inputType.toString(),
'inputType': inputType.toJSON(),
'obscureText': obscureText,
'autocorrect': autocorrect,
'actionLabel': actionLabel,
......
......@@ -303,7 +303,7 @@ class EditableText extends StatefulWidget {
properties.add(new DoubleProperty('textScaleFactor', textScaleFactor, defaultValue: null));
properties.add(new IntProperty('maxLines', maxLines, defaultValue: 1));
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() {
expect(configuration.actionLabel, null);
});
test('serializes to JSON', () async {
test('text serializes to JSON', () async {
const TextInputConfiguration configuration = const TextInputConfiguration(
inputType: TextInputType.number,
inputType: TextInputType.text,
obscureText: true,
autocorrect: false,
actionLabel: 'xyzzy'
actionLabel: 'xyzzy',
);
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['autocorrect'], false);
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() {
tester.firstWidget(find.byType(EditableText));
expect(editableText.maxLines, equals(1));
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'));
});
......@@ -88,7 +87,7 @@ void main() {
controller.text = 'test';
await tester.idle();
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'));
});
......@@ -113,7 +112,7 @@ void main() {
controller.text = 'test';
await tester.idle();
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'));
});
......@@ -137,7 +136,7 @@ void main() {
controller.text = 'test';
await tester.idle();
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'));
});
......@@ -161,7 +160,7 @@ void main() {
controller.text = 'test';
await tester.idle();
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'));
});
......
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