diff --git a/examples/mirai_gallery/assets/json/text_field_example.json b/examples/mirai_gallery/assets/json/text_field_example.json index a678689f..5d4b1594 100644 --- a/examples/mirai_gallery/assets/json/text_field_example.json +++ b/examples/mirai_gallery/assets/json/text_field_example.json @@ -47,6 +47,12 @@ }, "labelText": "Name*" }, + "inputFormatters": [ + { + "type": "allow", + "rule": "[a-zA-Z]" + } + ], "readOnly": false, "enabled": true }, @@ -75,6 +81,11 @@ }, "labelText": "Phone number*" }, + "inputFormatters": [ + { + "type": "digitsOnly" + } + ], "readOnly": false, "enabled": true }, @@ -110,6 +121,41 @@ "type": "sizedBox", "height": 24 }, + { + "type": "textField", + "maxLines": 1, + "keyboardType": "text", + "textInputAction": "done", + "textAlign": "start", + "textCapitalization": "none", + "textDirection": "ltr", + "textAlignVertical": "top", + "obscureText": false, + "decoration": { + "filled": true, + "icon": { + "type": "icon", + "iconType": "material", + "icon": "credit_card", + "size": 24 + }, + "hintText": "XXXX XXXX XXXX XXXX", + "labelText": "Credit Card" + }, + "inputFormatters": [ + { + "type": "masked", + "rule": "XXXX-XXXX-XXXX-XXXX", + "value": "-" + } + ], + "readOnly": false, + "enabled": true + }, + { + "type": "sizedBox", + "height": 24 + }, { "type": "sizedBox", "height": 100, diff --git a/packages/mirai/lib/src/parsers/mirai_input_formatters/mirai_input_formatter.dart b/packages/mirai/lib/src/parsers/mirai_input_formatters/mirai_input_formatter.dart new file mode 100644 index 00000000..52f02ffe --- /dev/null +++ b/packages/mirai/lib/src/parsers/mirai_input_formatters/mirai_input_formatter.dart @@ -0,0 +1,17 @@ +import 'package:freezed_annotation/freezed_annotation.dart'; +import 'package:mirai/src/utils/input_formatters.dart'; + +part 'mirai_input_formatter.freezed.dart'; +part 'mirai_input_formatter.g.dart'; + +@freezed +class MiraiInputFormatter with _$MiraiInputFormatter { + const factory MiraiInputFormatter({ + required InputFormatterType type, + String? rule, + String? value, + }) = _MiraiInputFormatter; + + factory MiraiInputFormatter.fromJson(Map json) => + _$MiraiInputFormatterFromJson(json); +} diff --git a/packages/mirai/lib/src/parsers/mirai_input_formatters/mirai_input_formatter.freezed.dart b/packages/mirai/lib/src/parsers/mirai_input_formatters/mirai_input_formatter.freezed.dart new file mode 100644 index 00000000..50cf7141 --- /dev/null +++ b/packages/mirai/lib/src/parsers/mirai_input_formatters/mirai_input_formatter.freezed.dart @@ -0,0 +1,187 @@ +// coverage:ignore-file +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'mirai_input_formatter.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +T _$identity(T value) => value; + +final _privateConstructorUsedError = UnsupportedError( + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods'); + +MiraiInputFormatter _$MiraiInputFormatterFromJson(Map json) { + return _MiraiInputFormatter.fromJson(json); +} + +/// @nodoc +mixin _$MiraiInputFormatter { + InputFormatterType get type => throw _privateConstructorUsedError; + String? get rule => throw _privateConstructorUsedError; + String? get value => throw _privateConstructorUsedError; + + Map toJson() => throw _privateConstructorUsedError; + @JsonKey(ignore: true) + $MiraiInputFormatterCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $MiraiInputFormatterCopyWith<$Res> { + factory $MiraiInputFormatterCopyWith( + MiraiInputFormatter value, $Res Function(MiraiInputFormatter) then) = + _$MiraiInputFormatterCopyWithImpl<$Res, MiraiInputFormatter>; + @useResult + $Res call({InputFormatterType type, String? rule, String? value}); +} + +/// @nodoc +class _$MiraiInputFormatterCopyWithImpl<$Res, $Val extends MiraiInputFormatter> + implements $MiraiInputFormatterCopyWith<$Res> { + _$MiraiInputFormatterCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? type = null, + Object? rule = freezed, + Object? value = freezed, + }) { + return _then(_value.copyWith( + type: null == type + ? _value.type + : type // ignore: cast_nullable_to_non_nullable + as InputFormatterType, + rule: freezed == rule + ? _value.rule + : rule // ignore: cast_nullable_to_non_nullable + as String?, + value: freezed == value + ? _value.value + : value // ignore: cast_nullable_to_non_nullable + as String?, + ) as $Val); + } +} + +/// @nodoc +abstract class _$$_MiraiInputFormatterCopyWith<$Res> + implements $MiraiInputFormatterCopyWith<$Res> { + factory _$$_MiraiInputFormatterCopyWith(_$_MiraiInputFormatter value, + $Res Function(_$_MiraiInputFormatter) then) = + __$$_MiraiInputFormatterCopyWithImpl<$Res>; + @override + @useResult + $Res call({InputFormatterType type, String? rule, String? value}); +} + +/// @nodoc +class __$$_MiraiInputFormatterCopyWithImpl<$Res> + extends _$MiraiInputFormatterCopyWithImpl<$Res, _$_MiraiInputFormatter> + implements _$$_MiraiInputFormatterCopyWith<$Res> { + __$$_MiraiInputFormatterCopyWithImpl(_$_MiraiInputFormatter _value, + $Res Function(_$_MiraiInputFormatter) _then) + : super(_value, _then); + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? type = null, + Object? rule = freezed, + Object? value = freezed, + }) { + return _then(_$_MiraiInputFormatter( + type: null == type + ? _value.type + : type // ignore: cast_nullable_to_non_nullable + as InputFormatterType, + rule: freezed == rule + ? _value.rule + : rule // ignore: cast_nullable_to_non_nullable + as String?, + value: freezed == value + ? _value.value + : value // ignore: cast_nullable_to_non_nullable + as String?, + )); + } +} + +/// @nodoc +@JsonSerializable() +class _$_MiraiInputFormatter implements _MiraiInputFormatter { + const _$_MiraiInputFormatter({required this.type, this.rule, this.value}); + + factory _$_MiraiInputFormatter.fromJson(Map json) => + _$$_MiraiInputFormatterFromJson(json); + + @override + final InputFormatterType type; + @override + final String? rule; + @override + final String? value; + + @override + String toString() { + return 'MiraiInputFormatter(type: $type, rule: $rule, value: $value)'; + } + + @override + bool operator ==(dynamic other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$_MiraiInputFormatter && + (identical(other.type, type) || other.type == type) && + (identical(other.rule, rule) || other.rule == rule) && + (identical(other.value, value) || other.value == value)); + } + + @JsonKey(ignore: true) + @override + int get hashCode => Object.hash(runtimeType, type, rule, value); + + @JsonKey(ignore: true) + @override + @pragma('vm:prefer-inline') + _$$_MiraiInputFormatterCopyWith<_$_MiraiInputFormatter> get copyWith => + __$$_MiraiInputFormatterCopyWithImpl<_$_MiraiInputFormatter>( + this, _$identity); + + @override + Map toJson() { + return _$$_MiraiInputFormatterToJson( + this, + ); + } +} + +abstract class _MiraiInputFormatter implements MiraiInputFormatter { + const factory _MiraiInputFormatter( + {required final InputFormatterType type, + final String? rule, + final String? value}) = _$_MiraiInputFormatter; + + factory _MiraiInputFormatter.fromJson(Map json) = + _$_MiraiInputFormatter.fromJson; + + @override + InputFormatterType get type; + @override + String? get rule; + @override + String? get value; + @override + @JsonKey(ignore: true) + _$$_MiraiInputFormatterCopyWith<_$_MiraiInputFormatter> get copyWith => + throw _privateConstructorUsedError; +} diff --git a/packages/mirai/lib/src/parsers/mirai_input_formatters/mirai_input_formatter.g.dart b/packages/mirai/lib/src/parsers/mirai_input_formatters/mirai_input_formatter.g.dart new file mode 100644 index 00000000..239a3187 --- /dev/null +++ b/packages/mirai/lib/src/parsers/mirai_input_formatters/mirai_input_formatter.g.dart @@ -0,0 +1,31 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'mirai_input_formatter.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +_$_MiraiInputFormatter _$$_MiraiInputFormatterFromJson( + Map json) => + _$_MiraiInputFormatter( + type: $enumDecode(_$InputFormatterTypeEnumMap, json['type']), + rule: json['rule'] as String?, + value: json['value'] as String?, + ); + +Map _$$_MiraiInputFormatterToJson( + _$_MiraiInputFormatter instance) => + { + 'type': _$InputFormatterTypeEnumMap[instance.type]!, + 'rule': instance.rule, + 'value': instance.value, + }; + +const _$InputFormatterTypeEnumMap = { + InputFormatterType.digitsOnly: 'digitsOnly', + InputFormatterType.singleLineFormatter: 'singleLineFormatter', + InputFormatterType.allow: 'allow', + InputFormatterType.deny: 'deny', + InputFormatterType.masked: 'masked', +}; diff --git a/packages/mirai/lib/src/parsers/mirai_text_field/mirai_text_field.dart b/packages/mirai/lib/src/parsers/mirai_text_field/mirai_text_field.dart index 1052f3cc..ac33079a 100644 --- a/packages/mirai/lib/src/parsers/mirai_text_field/mirai_text_field.dart +++ b/packages/mirai/lib/src/parsers/mirai_text_field/mirai_text_field.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:mirai/src/parsers/mirai_input_decoration/mirai_input_decoration.dart'; +import 'package:mirai/src/parsers/mirai_input_formatters/mirai_input_formatter.dart'; import 'package:mirai/src/parsers/mirai_text_style/mirai_text_style.dart'; import 'package:mirai/src/utils/text_input_utils.dart'; @@ -36,6 +37,7 @@ class MiraiTextField with _$MiraiTextField { double? cursorHeight, String? cursorColor, String? hintText, + @Default([]) List inputFormatters, }) = _MiraiTextField; factory MiraiTextField.fromJson(Map json) => diff --git a/packages/mirai/lib/src/parsers/mirai_text_field/mirai_text_field.freezed.dart b/packages/mirai/lib/src/parsers/mirai_text_field/mirai_text_field.freezed.dart index 01df79f7..2032a40e 100644 --- a/packages/mirai/lib/src/parsers/mirai_text_field/mirai_text_field.freezed.dart +++ b/packages/mirai/lib/src/parsers/mirai_text_field/mirai_text_field.freezed.dart @@ -46,6 +46,8 @@ mixin _$MiraiTextField { double? get cursorHeight => throw _privateConstructorUsedError; String? get cursorColor => throw _privateConstructorUsedError; String? get hintText => throw _privateConstructorUsedError; + List get inputFormatters => + throw _privateConstructorUsedError; Map toJson() => throw _privateConstructorUsedError; @JsonKey(ignore: true) @@ -83,7 +85,8 @@ abstract class $MiraiTextFieldCopyWith<$Res> { double cursorWidth, double? cursorHeight, String? cursorColor, - String? hintText}); + String? hintText, + List inputFormatters}); $MiraiInputDecorationCopyWith<$Res>? get decoration; $MiraiTextStyleCopyWith<$Res>? get style; @@ -126,6 +129,7 @@ class _$MiraiTextFieldCopyWithImpl<$Res, $Val extends MiraiTextField> Object? cursorHeight = freezed, Object? cursorColor = freezed, Object? hintText = freezed, + Object? inputFormatters = null, }) { return _then(_value.copyWith( decoration: freezed == decoration @@ -224,6 +228,10 @@ class _$MiraiTextFieldCopyWithImpl<$Res, $Val extends MiraiTextField> ? _value.hintText : hintText // ignore: cast_nullable_to_non_nullable as String?, + inputFormatters: null == inputFormatters + ? _value.inputFormatters + : inputFormatters // ignore: cast_nullable_to_non_nullable + as List, ) as $Val); } @@ -284,7 +292,8 @@ abstract class _$$_MiraiTextFieldCopyWith<$Res> double cursorWidth, double? cursorHeight, String? cursorColor, - String? hintText}); + String? hintText, + List inputFormatters}); @override $MiraiInputDecorationCopyWith<$Res>? get decoration; @@ -327,6 +336,7 @@ class __$$_MiraiTextFieldCopyWithImpl<$Res> Object? cursorHeight = freezed, Object? cursorColor = freezed, Object? hintText = freezed, + Object? inputFormatters = null, }) { return _then(_$_MiraiTextField( decoration: freezed == decoration @@ -422,6 +432,10 @@ class __$$_MiraiTextFieldCopyWithImpl<$Res> ? _value.hintText : hintText // ignore: cast_nullable_to_non_nullable as String?, + inputFormatters: null == inputFormatters + ? _value._inputFormatters + : inputFormatters // ignore: cast_nullable_to_non_nullable + as List, )); } } @@ -453,7 +467,9 @@ class _$_MiraiTextField implements _MiraiTextField { this.cursorWidth = 2, this.cursorHeight, this.cursorColor, - this.hintText}); + this.hintText, + final List inputFormatters = const []}) + : _inputFormatters = inputFormatters; factory _$_MiraiTextField.fromJson(Map json) => _$$_MiraiTextFieldFromJson(json); @@ -516,10 +532,18 @@ class _$_MiraiTextField implements _MiraiTextField { final String? cursorColor; @override final String? hintText; + final List _inputFormatters; + @override + @JsonKey() + List get inputFormatters { + if (_inputFormatters is EqualUnmodifiableListView) return _inputFormatters; + // ignore: implicit_dynamic_type + return EqualUnmodifiableListView(_inputFormatters); + } @override String toString() { - return 'MiraiTextField(decoration: $decoration, initialValue: $initialValue, keyboardType: $keyboardType, textInputAction: $textInputAction, textCapitalization: $textCapitalization, style: $style, textAlign: $textAlign, textAlignVertical: $textAlignVertical, textDirection: $textDirection, readOnly: $readOnly, showCursor: $showCursor, expands: $expands, autofocus: $autofocus, obscuringCharacter: $obscuringCharacter, maxLines: $maxLines, minLines: $minLines, maxLength: $maxLength, obscureText: $obscureText, enableSuggestions: $enableSuggestions, enabled: $enabled, cursorWidth: $cursorWidth, cursorHeight: $cursorHeight, cursorColor: $cursorColor, hintText: $hintText)'; + return 'MiraiTextField(decoration: $decoration, initialValue: $initialValue, keyboardType: $keyboardType, textInputAction: $textInputAction, textCapitalization: $textCapitalization, style: $style, textAlign: $textAlign, textAlignVertical: $textAlignVertical, textDirection: $textDirection, readOnly: $readOnly, showCursor: $showCursor, expands: $expands, autofocus: $autofocus, obscuringCharacter: $obscuringCharacter, maxLines: $maxLines, minLines: $minLines, maxLength: $maxLength, obscureText: $obscureText, enableSuggestions: $enableSuggestions, enabled: $enabled, cursorWidth: $cursorWidth, cursorHeight: $cursorHeight, cursorColor: $cursorColor, hintText: $hintText, inputFormatters: $inputFormatters)'; } @override @@ -571,7 +595,9 @@ class _$_MiraiTextField implements _MiraiTextField { (identical(other.cursorColor, cursorColor) || other.cursorColor == cursorColor) && (identical(other.hintText, hintText) || - other.hintText == hintText)); + other.hintText == hintText) && + const DeepCollectionEquality() + .equals(other._inputFormatters, _inputFormatters)); } @JsonKey(ignore: true) @@ -601,7 +627,8 @@ class _$_MiraiTextField implements _MiraiTextField { cursorWidth, cursorHeight, cursorColor, - hintText + hintText, + const DeepCollectionEquality().hash(_inputFormatters) ]); @JsonKey(ignore: true) @@ -643,7 +670,8 @@ abstract class _MiraiTextField implements MiraiTextField { final double cursorWidth, final double? cursorHeight, final String? cursorColor, - final String? hintText}) = _$_MiraiTextField; + final String? hintText, + final List inputFormatters}) = _$_MiraiTextField; factory _MiraiTextField.fromJson(Map json) = _$_MiraiTextField.fromJson; @@ -697,6 +725,8 @@ abstract class _MiraiTextField implements MiraiTextField { @override String? get hintText; @override + List get inputFormatters; + @override @JsonKey(ignore: true) _$$_MiraiTextFieldCopyWith<_$_MiraiTextField> get copyWith => throw _privateConstructorUsedError; diff --git a/packages/mirai/lib/src/parsers/mirai_text_field/mirai_text_field.g.dart b/packages/mirai/lib/src/parsers/mirai_text_field/mirai_text_field.g.dart index 8a61f126..61c71d6b 100644 --- a/packages/mirai/lib/src/parsers/mirai_text_field/mirai_text_field.g.dart +++ b/packages/mirai/lib/src/parsers/mirai_text_field/mirai_text_field.g.dart @@ -44,6 +44,11 @@ _$_MiraiTextField _$$_MiraiTextFieldFromJson(Map json) => cursorHeight: (json['cursorHeight'] as num?)?.toDouble(), cursorColor: json['cursorColor'] as String?, hintText: json['hintText'] as String?, + inputFormatters: (json['inputFormatters'] as List?) + ?.map((e) => + MiraiInputFormatter.fromJson(e as Map)) + .toList() ?? + const [], ); Map _$$_MiraiTextFieldToJson(_$_MiraiTextField instance) => @@ -74,6 +79,7 @@ Map _$$_MiraiTextFieldToJson(_$_MiraiTextField instance) => 'cursorHeight': instance.cursorHeight, 'cursorColor': instance.cursorColor, 'hintText': instance.hintText, + 'inputFormatters': instance.inputFormatters, }; const _$MiraiTextInputTypeEnumMap = { diff --git a/packages/mirai/lib/src/parsers/mirai_text_field/mirai_text_field_parser.dart b/packages/mirai/lib/src/parsers/mirai_text_field/mirai_text_field_parser.dart index e9e8a758..aaeb053c 100644 --- a/packages/mirai/lib/src/parsers/mirai_text_field/mirai_text_field_parser.dart +++ b/packages/mirai/lib/src/parsers/mirai_text_field/mirai_text_field_parser.dart @@ -4,6 +4,7 @@ import 'package:mirai/src/parsers/mirai_input_decoration/mirai_input_decoration. import 'package:mirai/src/parsers/mirai_text_field/mirai_text_field.dart'; import 'package:mirai/src/parsers/mirai_text_style/mirai_text_style.dart'; import 'package:mirai/src/utils/color_utils.dart'; +import 'package:mirai/src/utils/input_formatters.dart'; import 'package:mirai/src/utils/widget_type.dart'; class MiraiTextFieldParser extends MiraiParser { @@ -51,6 +52,9 @@ class MiraiTextFieldParser extends MiraiParser { cursorColor: model.cursorColor?.toColor, style: model.style?.parse, decoration: model.decoration?.parse(context), + inputFormatters: model.inputFormatters.map((formatter) { + return InputFormatters.format(formatter); + }).toList(), ); } } diff --git a/packages/mirai/lib/src/parsers/mirai_text_form_field/mirai_text_form_field.dart b/packages/mirai/lib/src/parsers/mirai_text_form_field/mirai_text_form_field.dart index ab6fa584..6fb0566c 100644 --- a/packages/mirai/lib/src/parsers/mirai_text_form_field/mirai_text_form_field.dart +++ b/packages/mirai/lib/src/parsers/mirai_text_form_field/mirai_text_form_field.dart @@ -2,6 +2,7 @@ import 'package:flutter/services.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:mirai/src/parsers/mirai_edge_insets/mirai_edge_insets.dart'; import 'package:mirai/src/parsers/mirai_input_decoration/mirai_input_decoration.dart'; +import 'package:mirai/src/parsers/mirai_input_formatters/mirai_input_formatter.dart'; import 'package:mirai/src/parsers/mirai_text_style/mirai_text_style.dart'; import 'package:mirai/src/utils/text_input_utils.dart'; @@ -46,6 +47,7 @@ class MiraiTextFormField with _$MiraiTextFormField { double? cursorHeight, String? cursorColor, String? hintText, + @Default([]) List inputFormatters, }) = _MiraiTextFormField; factory MiraiTextFormField.fromJson(Map json) => diff --git a/packages/mirai/lib/src/parsers/mirai_text_form_field/mirai_text_form_field.freezed.dart b/packages/mirai/lib/src/parsers/mirai_text_form_field/mirai_text_form_field.freezed.dart index 1a6c8a68..5beb3707 100644 --- a/packages/mirai/lib/src/parsers/mirai_text_form_field/mirai_text_form_field.freezed.dart +++ b/packages/mirai/lib/src/parsers/mirai_text_form_field/mirai_text_form_field.freezed.dart @@ -55,6 +55,8 @@ mixin _$MiraiTextFormField { double? get cursorHeight => throw _privateConstructorUsedError; String? get cursorColor => throw _privateConstructorUsedError; String? get hintText => throw _privateConstructorUsedError; + List get inputFormatters => + throw _privateConstructorUsedError; Map toJson() => throw _privateConstructorUsedError; @JsonKey(ignore: true) @@ -100,7 +102,8 @@ abstract class $MiraiTextFormFieldCopyWith<$Res> { double cursorWidth, double? cursorHeight, String? cursorColor, - String? hintText}); + String? hintText, + List inputFormatters}); $MiraiInputDecorationCopyWith<$Res>? get decoration; $MiraiTextStyleCopyWith<$Res>? get style; @@ -152,6 +155,7 @@ class _$MiraiTextFormFieldCopyWithImpl<$Res, $Val extends MiraiTextFormField> Object? cursorHeight = freezed, Object? cursorColor = freezed, Object? hintText = freezed, + Object? inputFormatters = null, }) { return _then(_value.copyWith( decoration: freezed == decoration @@ -282,6 +286,10 @@ class _$MiraiTextFormFieldCopyWithImpl<$Res, $Val extends MiraiTextFormField> ? _value.hintText : hintText // ignore: cast_nullable_to_non_nullable as String?, + inputFormatters: null == inputFormatters + ? _value.inputFormatters + : inputFormatters // ignore: cast_nullable_to_non_nullable + as List, ) as $Val); } @@ -358,7 +366,8 @@ abstract class _$$_MiraiTextFormFieldCopyWith<$Res> double cursorWidth, double? cursorHeight, String? cursorColor, - String? hintText}); + String? hintText, + List inputFormatters}); @override $MiraiInputDecorationCopyWith<$Res>? get decoration; @@ -411,6 +420,7 @@ class __$$_MiraiTextFormFieldCopyWithImpl<$Res> Object? cursorHeight = freezed, Object? cursorColor = freezed, Object? hintText = freezed, + Object? inputFormatters = null, }) { return _then(_$_MiraiTextFormField( decoration: freezed == decoration @@ -541,6 +551,10 @@ class __$$_MiraiTextFormFieldCopyWithImpl<$Res> ? _value.hintText : hintText // ignore: cast_nullable_to_non_nullable as String?, + inputFormatters: null == inputFormatters + ? _value._inputFormatters + : inputFormatters // ignore: cast_nullable_to_non_nullable + as List, )); } } @@ -581,7 +595,9 @@ class _$_MiraiTextFormField implements _MiraiTextFormField { this.cursorWidth = 2, this.cursorHeight, this.cursorColor, - this.hintText}); + this.hintText, + final List inputFormatters = const []}) + : _inputFormatters = inputFormatters; factory _$_MiraiTextFormField.fromJson(Map json) => _$$_MiraiTextFormFieldFromJson(json); @@ -662,10 +678,18 @@ class _$_MiraiTextFormField implements _MiraiTextFormField { final String? cursorColor; @override final String? hintText; + final List _inputFormatters; + @override + @JsonKey() + List get inputFormatters { + if (_inputFormatters is EqualUnmodifiableListView) return _inputFormatters; + // ignore: implicit_dynamic_type + return EqualUnmodifiableListView(_inputFormatters); + } @override String toString() { - return 'MiraiTextFormField(decoration: $decoration, initialValue: $initialValue, keyboardType: $keyboardType, textInputAction: $textInputAction, textCapitalization: $textCapitalization, style: $style, textAlign: $textAlign, textAlignVertical: $textAlignVertical, textDirection: $textDirection, readOnly: $readOnly, showCursor: $showCursor, autofocus: $autofocus, obscuringCharacter: $obscuringCharacter, maxLines: $maxLines, minLines: $minLines, maxLength: $maxLength, obscureText: $obscureText, autocorrect: $autocorrect, smartDashesType: $smartDashesType, smartQuotesType: $smartQuotesType, maxLengthEnforcement: $maxLengthEnforcement, expands: $expands, keyboardAppearance: $keyboardAppearance, scrollPadding: $scrollPadding, restorationId: $restorationId, enableIMEPersonalizedLearning: $enableIMEPersonalizedLearning, enableSuggestions: $enableSuggestions, enabled: $enabled, cursorWidth: $cursorWidth, cursorHeight: $cursorHeight, cursorColor: $cursorColor, hintText: $hintText)'; + return 'MiraiTextFormField(decoration: $decoration, initialValue: $initialValue, keyboardType: $keyboardType, textInputAction: $textInputAction, textCapitalization: $textCapitalization, style: $style, textAlign: $textAlign, textAlignVertical: $textAlignVertical, textDirection: $textDirection, readOnly: $readOnly, showCursor: $showCursor, autofocus: $autofocus, obscuringCharacter: $obscuringCharacter, maxLines: $maxLines, minLines: $minLines, maxLength: $maxLength, obscureText: $obscureText, autocorrect: $autocorrect, smartDashesType: $smartDashesType, smartQuotesType: $smartQuotesType, maxLengthEnforcement: $maxLengthEnforcement, expands: $expands, keyboardAppearance: $keyboardAppearance, scrollPadding: $scrollPadding, restorationId: $restorationId, enableIMEPersonalizedLearning: $enableIMEPersonalizedLearning, enableSuggestions: $enableSuggestions, enabled: $enabled, cursorWidth: $cursorWidth, cursorHeight: $cursorHeight, cursorColor: $cursorColor, hintText: $hintText, inputFormatters: $inputFormatters)'; } @override @@ -735,7 +759,9 @@ class _$_MiraiTextFormField implements _MiraiTextFormField { (identical(other.cursorColor, cursorColor) || other.cursorColor == cursorColor) && (identical(other.hintText, hintText) || - other.hintText == hintText)); + other.hintText == hintText) && + const DeepCollectionEquality() + .equals(other._inputFormatters, _inputFormatters)); } @JsonKey(ignore: true) @@ -773,7 +799,8 @@ class _$_MiraiTextFormField implements _MiraiTextFormField { cursorWidth, cursorHeight, cursorColor, - hintText + hintText, + const DeepCollectionEquality().hash(_inputFormatters) ]); @JsonKey(ignore: true) @@ -824,7 +851,8 @@ abstract class _MiraiTextFormField implements MiraiTextFormField { final double cursorWidth, final double? cursorHeight, final String? cursorColor, - final String? hintText}) = _$_MiraiTextFormField; + final String? hintText, + final List inputFormatters}) = _$_MiraiTextFormField; factory _MiraiTextFormField.fromJson(Map json) = _$_MiraiTextFormField.fromJson; @@ -894,6 +922,8 @@ abstract class _MiraiTextFormField implements MiraiTextFormField { @override String? get hintText; @override + List get inputFormatters; + @override @JsonKey(ignore: true) _$$_MiraiTextFormFieldCopyWith<_$_MiraiTextFormField> get copyWith => throw _privateConstructorUsedError; diff --git a/packages/mirai/lib/src/parsers/mirai_text_form_field/mirai_text_form_field.g.dart b/packages/mirai/lib/src/parsers/mirai_text_form_field/mirai_text_form_field.g.dart index 95a3edde..136f117d 100644 --- a/packages/mirai/lib/src/parsers/mirai_text_form_field/mirai_text_form_field.g.dart +++ b/packages/mirai/lib/src/parsers/mirai_text_form_field/mirai_text_form_field.g.dart @@ -61,6 +61,11 @@ _$_MiraiTextFormField _$$_MiraiTextFormFieldFromJson( cursorHeight: (json['cursorHeight'] as num?)?.toDouble(), cursorColor: json['cursorColor'] as String?, hintText: json['hintText'] as String?, + inputFormatters: (json['inputFormatters'] as List?) + ?.map((e) => + MiraiInputFormatter.fromJson(e as Map)) + .toList() ?? + const [], ); Map _$$_MiraiTextFormFieldToJson( @@ -101,6 +106,7 @@ Map _$$_MiraiTextFormFieldToJson( 'cursorHeight': instance.cursorHeight, 'cursorColor': instance.cursorColor, 'hintText': instance.hintText, + 'inputFormatters': instance.inputFormatters, }; const _$MiraiTextInputTypeEnumMap = { diff --git a/packages/mirai/lib/src/parsers/mirai_text_form_field/mirai_text_form_field_parser.dart b/packages/mirai/lib/src/parsers/mirai_text_form_field/mirai_text_form_field_parser.dart index 7606b948..ba114058 100644 --- a/packages/mirai/lib/src/parsers/mirai_text_form_field/mirai_text_form_field_parser.dart +++ b/packages/mirai/lib/src/parsers/mirai_text_form_field/mirai_text_form_field_parser.dart @@ -5,6 +5,7 @@ import 'package:mirai/src/parsers/mirai_input_decoration/mirai_input_decoration. import 'package:mirai/src/parsers/mirai_text_form_field/mirai_text_form_field.dart'; import 'package:mirai/src/parsers/mirai_text_style/mirai_text_style.dart'; import 'package:mirai/src/utils/color_utils.dart'; +import 'package:mirai/src/utils/input_formatters.dart'; import 'package:mirai/src/utils/widget_type.dart'; class MiraiTextFormFieldParser extends MiraiParser { @@ -59,6 +60,9 @@ class MiraiTextFormFieldParser extends MiraiParser { cursorColor: model.cursorColor?.toColor, style: model.style?.parse, decoration: model.decoration.parse(context), + inputFormatters: model.inputFormatters.map((formatter) { + return InputFormatters.format(formatter); + }).toList(), ); } } diff --git a/packages/mirai/lib/src/utils/input_formatters.dart b/packages/mirai/lib/src/utils/input_formatters.dart new file mode 100644 index 00000000..efa63244 --- /dev/null +++ b/packages/mirai/lib/src/utils/input_formatters.dart @@ -0,0 +1,102 @@ +import 'package:flutter/services.dart'; +import 'package:mirai/src/parsers/mirai_input_formatters/mirai_input_formatter.dart'; + +enum InputFormatterType { + digitsOnly, + singleLineFormatter, + allow, + deny, + masked, +} + +class InputFormatters { + static TextInputFormatter format(MiraiInputFormatter inputFormatter) { + try { + switch (inputFormatter.type) { + case InputFormatterType.digitsOnly: + return FilteringTextInputFormatter.digitsOnly; + + case InputFormatterType.singleLineFormatter: + return FilteringTextInputFormatter.singleLineFormatter; + + case InputFormatterType.allow: + return FilteringTextInputFormatter.allow( + RegExp(inputFormatter.rule ?? "")); + + case InputFormatterType.deny: + return FilteringTextInputFormatter.allow( + RegExp(inputFormatter.rule ?? "")); + + case InputFormatterType.masked: + return MaskedTextInputFormatter( + mask: inputFormatter.rule ?? "", + separator: inputFormatter.value ?? "-", + ); + } + } catch (_) { + return FilteringTextInputFormatter.allow(RegExp('')); + } + } +} + +class CardNumberFormatter extends TextInputFormatter { + @override + TextEditingValue formatEditUpdate( + TextEditingValue oldValue, + TextEditingValue newValue, + ) { + var inputText = newValue.text; + + if (newValue.selection.baseOffset == 0) { + return newValue; + } + + var bufferString = StringBuffer(); + for (int i = 0; i < inputText.length; i++) { + bufferString.write(inputText[i]); + var nonZeroIndexValue = i + 1; + if (nonZeroIndexValue % 4 == 0 && nonZeroIndexValue != inputText.length) { + bufferString.write(' '); + } + } + + var string = bufferString.toString(); + return newValue.copyWith( + text: string, + selection: TextSelection.collapsed( + offset: string.length, + ), + ); + } +} + +class MaskedTextInputFormatter extends TextInputFormatter { + final String mask; + final String separator; + + MaskedTextInputFormatter({ + required this.mask, + required this.separator, + }); + + @override + TextEditingValue formatEditUpdate( + TextEditingValue oldValue, TextEditingValue newValue) { + if (newValue.text.isNotEmpty) { + if (newValue.text.length > oldValue.text.length) { + if (newValue.text.length > mask.length) return oldValue; + if (newValue.text.length < mask.length && + mask[newValue.text.length - 1] == separator) { + return TextEditingValue( + text: + '${oldValue.text}$separator${newValue.text.substring(newValue.text.length - 1)}', + selection: TextSelection.collapsed( + offset: newValue.selection.end + 1, + ), + ); + } + } + } + return newValue; + } +}