From d4850333c4d76db7a9601330efc5fd742f4e5b1b Mon Sep 17 00:00:00 2001 From: Ahmed Fwela Date: Mon, 29 Apr 2024 05:47:19 +0300 Subject: [PATCH 1/3] support phone_form_field v9.0.0 --- .../lib/reactive_phone_form_field.dart | 5 +- .../lib/src/reactive_phone_form_field.dart | 114 ++++------------ .../src/validators/validation_message.dart | 9 +- .../src/validators/validator/_exports.dart | 3 + .../validator/country_phone_validator.dart | 24 ++++ .../validators/validator/phone_validator.dart | 127 ------------------ .../validator/required_validator.dart | 16 +-- .../validator/valid_fixed_line_validator.dart | 49 ------- .../validator/valid_mobile_validator.dart | 26 ---- .../validators/validator/valid_validator.dart | 32 +++-- .../lib/src/validators/validators.dart | 25 ++-- 11 files changed, 101 insertions(+), 329 deletions(-) create mode 100644 packages/reactive_phone_form_field/lib/src/validators/validator/_exports.dart create mode 100644 packages/reactive_phone_form_field/lib/src/validators/validator/country_phone_validator.dart delete mode 100644 packages/reactive_phone_form_field/lib/src/validators/validator/phone_validator.dart delete mode 100644 packages/reactive_phone_form_field/lib/src/validators/validator/valid_fixed_line_validator.dart delete mode 100644 packages/reactive_phone_form_field/lib/src/validators/validator/valid_mobile_validator.dart diff --git a/packages/reactive_phone_form_field/lib/reactive_phone_form_field.dart b/packages/reactive_phone_form_field/lib/reactive_phone_form_field.dart index 161081f..20ffd10 100644 --- a/packages/reactive_phone_form_field/lib/reactive_phone_form_field.dart +++ b/packages/reactive_phone_form_field/lib/reactive_phone_form_field.dart @@ -3,7 +3,4 @@ library reactive_phone_form_field; export 'src/reactive_phone_form_field.dart'; export 'src/validators/validators.dart'; export 'src/validators/validation_message.dart'; -export 'src/validators/validator/required_validator.dart'; -export 'src/validators/validator/valid_fixed_line_validator.dart'; -export 'src/validators/validator/valid_mobile_validator.dart'; -export 'src/validators/validator/valid_validator.dart'; \ No newline at end of file +export 'src/validators/validator/_exports.dart'; diff --git a/packages/reactive_phone_form_field/lib/src/reactive_phone_form_field.dart b/packages/reactive_phone_form_field/lib/src/reactive_phone_form_field.dart index 1702a0c..feffdc9 100644 --- a/packages/reactive_phone_form_field/lib/src/reactive_phone_form_field.dart +++ b/packages/reactive_phone_form_field/lib/src/reactive_phone_form_field.dart @@ -20,7 +20,8 @@ export 'package:phone_form_field/phone_form_field.dart'; /// /// A [ReactiveForm] ancestor is required. /// -class ReactivePhoneFormField extends ReactiveFormField { +class ReactivePhoneFormField + extends ReactiveFocusableFormField { /// Creates a [ReactivePhoneFormField] that contains a [PhoneFormField]. /// /// Can optionally provide a [formControl] to bind this widget to a control. @@ -85,32 +86,24 @@ class ReactivePhoneFormField extends ReactiveFormField { /// For documentation about the various parameters, see the [PhoneFormField] class /// and [PhoneFormField], the constructor. ReactivePhoneFormField({ - Key? key, - String? formControlName, - FormControl? formControl, - Map? validationMessages, - ControlValueAccessor? valueAccessor, - ShowErrorsFunction? showErrors, - + super.key, + super.formControlName, + super.formControl, + super.validationMessages, + super.valueAccessor, + super.showErrors, + super.focusNode, //////////////////////////////////////////////////////////////////////////// - bool shouldFormat = true, - bool enabled = true, - bool showFlagInInput = true, CountrySelectorNavigator countrySelectorNavigator = - const CountrySelectorNavigator.searchDelegate(), + const CountrySelectorNavigator.page(), Function(PhoneNumber?)? onSaved, - IsoCode defaultCountry = IsoCode.US, - PhoneNumber? initialValue, - double flagSize = 16, InputDecoration decoration = const InputDecoration(), TextInputType keyboardType = TextInputType.phone, TextInputAction? textInputAction, TextStyle? style, StrutStyle? strutStyle, - TextAlign textAlign = TextAlign.start, TextAlignVertical? textAlignVertical, bool autofocus = false, - bool readOnly = false, bool? showCursor, bool obscureText = false, String obscuringCharacter = '•', @@ -118,12 +111,6 @@ class ReactivePhoneFormField extends ReactiveFormField { SmartDashesType? smartDashesType, SmartQuotesType? smartQuotesType, bool enableSuggestions = true, - MaxLengthEnforcement? maxLengthEnforcement, - int? maxLines = 1, - int? minLines, - bool expands = false, - int? maxLength, - GestureTapCallback? onTap, VoidCallback? onEditingComplete, List? inputFormatters, double cursorWidth = 2.0, @@ -133,76 +120,58 @@ class ReactivePhoneFormField extends ReactiveFormField { Brightness? keyboardAppearance, EdgeInsets scrollPadding = const EdgeInsets.all(20.0), bool enableInteractiveSelection = true, - InputCounterWidgetBuilder? buildCounter, ScrollPhysics? scrollPhysics, VoidCallback? onSubmitted, - FocusNode? focusNode, Iterable? autofillHints, MouseCursor? mouseCursor, - DragStartBehavior dragStartBehavior = DragStartBehavior.start, AppPrivateCommandCallback? onAppPrivateCommand, String? restorationId, ScrollController? scrollController, TextSelectionControls? selectionControls, ui.BoxHeightStyle selectionHeightStyle = ui.BoxHeightStyle.tight, ui.BoxWidthStyle selectionWidthStyle = ui.BoxWidthStyle.tight, - TextStyle? countryCodeStyle, + CountryButtonStyle countryButtonStyle = const CountryButtonStyle(), bool enableIMEPersonalizedLearning = true, bool isCountrySelectionEnabled = true, - bool isCountryChipPersistent = false, + bool isCountryButtonPersistent = false, + Widget Function(BuildContext, EditableTextState)? contextMenuBuilder, + Function(PointerDownEvent)? onTapOutside, }) : super( - key: key, - formControl: formControl, - formControlName: formControlName, - valueAccessor: valueAccessor, - validationMessages: validationMessages, - showErrors: showErrors, builder: (field) { final state = field as _ReactivePhoneFormFieldState; final effectiveDecoration = decoration .applyDefaults(Theme.of(state.context).inputDecorationTheme); - state._setFocusNode(focusNode); - return PhoneFormField( - countryCodeStyle: countryCodeStyle, + countryButtonStyle: countryButtonStyle, focusNode: state.focusNode, controller: state._textController, - shouldFormat: shouldFormat, onChanged: field.didChange, autofillHints: autofillHints, + contextMenuBuilder: contextMenuBuilder, autofocus: autofocus, enabled: field.control.enabled, - showFlagInInput: showFlagInInput, countrySelectorNavigator: countrySelectorNavigator, onSaved: onSaved, - defaultCountry: defaultCountry, decoration: effectiveDecoration.copyWith( errorText: field.errorText, enabled: field.control.enabled, ), cursorColor: cursorColor, autovalidateMode: AutovalidateMode.disabled, - flagSize: flagSize, onEditingComplete: onEditingComplete, restorationId: restorationId, keyboardType: keyboardType, textInputAction: textInputAction, style: style, strutStyle: strutStyle, - textAlign: textAlign, textAlignVertical: textAlignVertical, showCursor: showCursor, obscureText: obscureText, autocorrect: autocorrect, - smartDashesType: smartDashesType ?? - (obscureText - ? SmartDashesType.disabled - : SmartDashesType.enabled), - smartQuotesType: smartQuotesType ?? - (obscureText - ? SmartQuotesType.disabled - : SmartQuotesType.enabled), + smartDashesType: smartDashesType, + smartQuotesType: smartQuotesType, + onTapOutside: onTapOutside, enableSuggestions: enableSuggestions, onSubmitted: onSubmitted != null ? (_) => onSubmitted() : null, inputFormatters: inputFormatters, @@ -222,7 +191,7 @@ class ReactivePhoneFormField extends ReactiveFormField { selectionWidthStyle: selectionWidthStyle, enableIMEPersonalizedLearning: enableIMEPersonalizedLearning, isCountrySelectionEnabled: isCountrySelectionEnabled, - isCountryChipPersistent: isCountryChipPersistent, + isCountryButtonPersistent: isCountryButtonPersistent, ); }, ); @@ -233,37 +202,24 @@ class ReactivePhoneFormField extends ReactiveFormField { } class _ReactivePhoneFormFieldState - extends ReactiveFormFieldState { + extends ReactiveFocusableFormFieldState { late PhoneController _textController; - FocusNode? _focusNode; - late FocusController _focusController; - @override - FocusNode get focusNode => _focusNode ?? _focusController.focusNode; + static const defaultPhone = PhoneNumber(isoCode: IsoCode.US, nsn: ''); @override void initState() { super.initState(); - _textController = PhoneController(value); - } - - @override - void subscribeControl() { - _registerFocusController(FocusController()); - super.subscribeControl(); - } - - @override - void unsubscribeControl() { - _unregisterFocusController(); - super.unsubscribeControl(); + _textController = PhoneController( + initialValue: value ?? defaultPhone, + ); } @override void onControlValueChanged(dynamic value) { if (value == null) { - _textController.value = null; + _textController.value = const PhoneNumber(isoCode: IsoCode.US, nsn: ''); } else if (value is PhoneNumber) { _textController.value = PhoneNumber( isoCode: value.isoCode, @@ -274,27 +230,9 @@ class _ReactivePhoneFormFieldState super.onControlValueChanged(value); } - void _registerFocusController(FocusController focusController) { - _focusController = focusController; - control.registerFocusController(focusController); - } - - void _unregisterFocusController() { - control.unregisterFocusController(_focusController); - _focusController.dispose(); - } - @override void dispose() { _textController.dispose(); super.dispose(); } - - void _setFocusNode(FocusNode? focusNode) { - if (_focusNode != focusNode) { - _focusNode = focusNode; - _unregisterFocusController(); - _registerFocusController(FocusController(focusNode: _focusNode)); - } - } } diff --git a/packages/reactive_phone_form_field/lib/src/validators/validation_message.dart b/packages/reactive_phone_form_field/lib/src/validators/validation_message.dart index 4004d63..f5a0645 100644 --- a/packages/reactive_phone_form_field/lib/src/validators/validation_message.dart +++ b/packages/reactive_phone_form_field/lib/src/validators/validation_message.dart @@ -1,4 +1,9 @@ class PhoneValidationMessage { - static const String valid = 'phone.valid'; - static const String validMobile = 'phone.validMobile'; + static const String required = 'phone.required'; + static const String invalidPhoneNumber = 'phone.invalidPhoneNumber'; + static const String invalidCountry = 'phone.invalidCountry'; + static const String invalidMobilePhoneNumber = + 'phone.invalidMobilePhoneNumber'; + static const String invalidFixedLinePhoneNumber = + 'phone.invalidFixedLinePhoneNumber'; } diff --git a/packages/reactive_phone_form_field/lib/src/validators/validator/_exports.dart b/packages/reactive_phone_form_field/lib/src/validators/validator/_exports.dart new file mode 100644 index 0000000..895a2f0 --- /dev/null +++ b/packages/reactive_phone_form_field/lib/src/validators/validator/_exports.dart @@ -0,0 +1,3 @@ +export 'country_phone_validator.dart'; +export 'required_validator.dart'; +export 'valid_validator.dart'; diff --git a/packages/reactive_phone_form_field/lib/src/validators/validator/country_phone_validator.dart b/packages/reactive_phone_form_field/lib/src/validators/validator/country_phone_validator.dart new file mode 100644 index 0000000..e1c83be --- /dev/null +++ b/packages/reactive_phone_form_field/lib/src/validators/validator/country_phone_validator.dart @@ -0,0 +1,24 @@ +import 'package:phone_form_field/phone_form_field.dart'; +import 'package:reactive_forms/reactive_forms.dart'; +import 'package:reactive_phone_form_field/reactive_phone_form_field.dart'; + +class CountryPhoneValidator extends Validator { + final Set allowedCountries; + + const CountryPhoneValidator({ + required this.allowedCountries, + }); + + @override + Map? validate(AbstractControl control) { + final value = control.value; + + if (value == null || value.nsn.trim().isEmpty) return null; + + if (!allowedCountries.contains(value.isoCode)) { + return {PhoneValidationMessage.invalidCountry: allowedCountries}; + } + + return null; + } +} diff --git a/packages/reactive_phone_form_field/lib/src/validators/validator/phone_validator.dart b/packages/reactive_phone_form_field/lib/src/validators/validator/phone_validator.dart deleted file mode 100644 index 84ea40f..0000000 --- a/packages/reactive_phone_form_field/lib/src/validators/validator/phone_validator.dart +++ /dev/null @@ -1,127 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:phone_form_field/phone_form_field.dart'; - -typedef PhoneNumberInputValidator = String? Function(PhoneNumber? phoneNumber); - -abstract class PhoneValidator { - /// allow to compose several validators - /// Note that validator list order is important as first - /// validator failing will return according message. - static PhoneNumberInputValidator compose( - List validators, - ) { - return (valueCandidate) { - for (var validator in validators) { - final validatorResult = validator.call(valueCandidate); - if (validatorResult != null) { - return validatorResult; - } - } - return null; - }; - } - - static PhoneNumberInputValidator required( - { - /// custom error message - String? errorText, - }) { - return (PhoneNumber? valueCandidate) { - if (valueCandidate == null || (valueCandidate.nsn.trim().isEmpty)) { - return errorText; - } - return null; - }; - } - - static PhoneNumberInputValidator valid( - BuildContext context, { - /// custom error message - String? errorText, - }) { - return (PhoneNumber? valueCandidate) { - if (valueCandidate != null && - valueCandidate.nsn.isNotEmpty && - !valueCandidate.isValid()) { - return errorText ?? - PhoneFieldLocalization.of(context).invalidPhoneNumber; - } - return null; - }; - } - - static PhoneNumberInputValidator validType( - BuildContext context, - - /// expected phonetype - PhoneNumberType expectedType, { - /// custom error message - String? errorText, - }) { - return (PhoneNumber? valueCandidate) { - if (valueCandidate != null && - valueCandidate.nsn.isNotEmpty && - !valueCandidate.isValid(type: expectedType)) { - if (expectedType == PhoneNumberType.mobile) { - return errorText ?? - PhoneFieldLocalization.of(context).invalidMobilePhoneNumber; - } else if (expectedType == PhoneNumberType.fixedLine) { - return errorText ?? - PhoneFieldLocalization.of(context).invalidFixedLinePhoneNumber; - } - return errorText ?? - PhoneFieldLocalization.of(context).invalidPhoneNumber; - } - return null; - }; - } - - /// convenience shortcut method for - /// invalidType(context, PhoneNumberType.fixedLine, ...) - static PhoneNumberInputValidator validFixedLine( - BuildContext context, { - /// custom error message - String? errorText, - }) => - validType( - context, - PhoneNumberType.fixedLine, - errorText: errorText, - ); - - /// convenience shortcut method for - /// invalidType(context, PhoneNumberType.mobile, ...) - static PhoneNumberInputValidator validMobile( - BuildContext context, { - /// custom error message - String? errorText, - }) => - validType( - context, - PhoneNumberType.mobile, - errorText: errorText, - ); - - static PhoneNumberInputValidator validCountry( - BuildContext context, - - /// list of valid country isocode - List expectedCountries, { - /// custom error message - String? errorText, - }) { - return (PhoneNumber? valueCandidate) { - if (valueCandidate != null && - (valueCandidate.nsn.isNotEmpty) && - !expectedCountries.contains(valueCandidate.isoCode)) { - return errorText ?? PhoneFieldLocalization.of(context).invalidCountry; - } - return null; - }; - } - - @Deprecated('Use null instead') - static PhoneNumberInputValidator get none => (PhoneNumber? valueCandidate) { - return null; - }; -} diff --git a/packages/reactive_phone_form_field/lib/src/validators/validator/required_validator.dart b/packages/reactive_phone_form_field/lib/src/validators/validator/required_validator.dart index 7cb62c2..ea84892 100644 --- a/packages/reactive_phone_form_field/lib/src/validators/validator/required_validator.dart +++ b/packages/reactive_phone_form_field/lib/src/validators/validator/required_validator.dart @@ -1,23 +1,17 @@ import 'package:phone_form_field/phone_form_field.dart'; import 'package:reactive_forms/reactive_forms.dart'; +import 'package:reactive_phone_form_field/reactive_phone_form_field.dart'; import 'package:reactive_phone_form_field/src/validators/validation_message.dart'; -class RequiredPhoneValidator extends Validator { +class RequiredPhoneValidator extends Validator { const RequiredPhoneValidator(); - static const message = 'phone.required'; - @override - Map? validate(AbstractControl control) { - final error = {message: true}; + Map? validate(AbstractControl control) { final value = control.value; - if(value is! PhoneNumber?) { - return error; - } - - if (value == null || (value.nsn.trim().isEmpty)) { - return error; + if (value == null || value.nsn.trim().isEmpty) { + return {PhoneValidationMessage.required: true}; } return null; diff --git a/packages/reactive_phone_form_field/lib/src/validators/validator/valid_fixed_line_validator.dart b/packages/reactive_phone_form_field/lib/src/validators/validator/valid_fixed_line_validator.dart deleted file mode 100644 index c1e36e5..0000000 --- a/packages/reactive_phone_form_field/lib/src/validators/validator/valid_fixed_line_validator.dart +++ /dev/null @@ -1,49 +0,0 @@ -import 'package:phone_form_field/phone_form_field.dart'; -import 'package:reactive_forms/reactive_forms.dart'; -import 'package:reactive_phone_form_field/src/validators/validation_message.dart'; - -class ValidFixedLinePhoneValidator extends Validator { - const ValidFixedLinePhoneValidator(); - - static const message = 'phone.validFixedLine'; - - @override - Map? validate(AbstractControl control) { - final error = { message: true }; - - final value = control.value; - - if(value is! PhoneNumber?) { - return error; - } - - if ( - value.nsn.isNotEmpty && - !valueCandidate.isValid(type: expectedType)) { - if (expectedType == PhoneNumberType.mobile) { - return errorText ?? - PhoneFieldLocalization.of(context).invalidMobilePhoneNumber; - } else if (expectedType == PhoneNumberType.fixedLine) { - return errorText ?? - PhoneFieldLocalization.of(context).invalidFixedLinePhoneNumber; - } - return errorText ?? - PhoneFieldLocalization.of(context).invalidPhoneNumber; - } - return null; - - // if (value == null) { - // return null; - // } else if (control.value is PhoneNumber) { - // PhoneNumber? valueCandidate = control.value as PhoneNumber; - // - // if (PhoneValidator.validFixedLine().call(valueCandidate) == null) { - // return null; - // } else { - // return error; - // } - // } - - return null; - } -} diff --git a/packages/reactive_phone_form_field/lib/src/validators/validator/valid_mobile_validator.dart b/packages/reactive_phone_form_field/lib/src/validators/validator/valid_mobile_validator.dart deleted file mode 100644 index 65be76f..0000000 --- a/packages/reactive_phone_form_field/lib/src/validators/validator/valid_mobile_validator.dart +++ /dev/null @@ -1,26 +0,0 @@ -import 'package:phone_form_field/phone_form_field.dart'; -import 'package:reactive_forms/reactive_forms.dart'; -import 'package:reactive_phone_form_field/src/validators/validation_message.dart'; - -class ValidMobilePhoneValidator extends Validator { - const ValidMobilePhoneValidator(); - - @override - Map? validate(AbstractControl control) { - final error = {PhoneValidationMessage.validMobile: true}; - - if (control.value == null) { - return null; - } else if (control.value is PhoneNumber) { - PhoneNumber? valueCandidate = control.value as PhoneNumber; - - if (PhoneValidator.validMobile().call(valueCandidate) == null) { - return null; - } else { - return error; - } - } - - return null; - } -} diff --git a/packages/reactive_phone_form_field/lib/src/validators/validator/valid_validator.dart b/packages/reactive_phone_form_field/lib/src/validators/validator/valid_validator.dart index 315569d..033b6b8 100644 --- a/packages/reactive_phone_form_field/lib/src/validators/validator/valid_validator.dart +++ b/packages/reactive_phone_form_field/lib/src/validators/validator/valid_validator.dart @@ -3,23 +3,29 @@ import 'package:reactive_forms/reactive_forms.dart'; import 'package:reactive_phone_form_field/src/validators/validation_message.dart'; /// Validator that requires the control have a non-empty value. -class ValidPhoneValidator extends Validator { - const ValidPhoneValidator(); +class ValidPhoneValidator extends Validator { + const ValidPhoneValidator({this.type}); + + final PhoneNumberType? type; @override - Map? validate(AbstractControl control) { - final error = {PhoneValidationMessage.valid: true}; + Map? validate(AbstractControl control) { + final value = control.value; + + if (value == null || value.nsn.trim().isEmpty) return null; - if (control.value == null) { - return null; - } else if (control.value is PhoneNumber) { - PhoneNumber? valueCandidate = control.value as PhoneNumber; + if (!value.isValid(type: type)) { + final messageKey = switch (type) { + PhoneNumberType.mobile => + PhoneValidationMessage.invalidMobilePhoneNumber, + PhoneNumberType.fixedLine => + PhoneValidationMessage.invalidFixedLinePhoneNumber, + _ => PhoneValidationMessage.invalidPhoneNumber, + }; - if (PhoneValidator.valid().call(valueCandidate) == null) { - return null; - } else { - return error; - } + return { + messageKey: type, + }; } return null; diff --git a/packages/reactive_phone_form_field/lib/src/validators/validators.dart b/packages/reactive_phone_form_field/lib/src/validators/validators.dart index 54b7027..ff2fa82 100644 --- a/packages/reactive_phone_form_field/lib/src/validators/validators.dart +++ b/packages/reactive_phone_form_field/lib/src/validators/validators.dart @@ -1,25 +1,32 @@ -import 'package:reactive_forms/reactive_forms.dart'; +import 'package:phone_form_field/phone_form_field.dart'; import 'package:reactive_phone_form_field/src/validators/validator/required_validator.dart'; -import 'package:reactive_phone_form_field/src/validators/validator/valid_fixed_line_validator.dart'; -import 'package:reactive_phone_form_field/src/validators/validator/valid_mobile_validator.dart'; import 'package:reactive_phone_form_field/src/validators/validator/valid_validator.dart'; +import 'validator/country_phone_validator.dart'; + /// Provides a set of built-in validators that can be used by form controls. class PhoneValidators { /// Gets a validator that requires the control have a non-empty value. - static Validator get required => const RequiredPhoneValidator(); + static RequiredPhoneValidator get required => const RequiredPhoneValidator(); + + /// Gets a validator that requires the control's value pass an phone + /// validation test. + static ValidPhoneValidator get valid => const ValidPhoneValidator(); /// Gets a validator that requires the control's value pass an phone /// validation test. - static Validator get valid => const ValidPhoneValidator(); + static CountryPhoneValidator forCountries({ + required Set allowedCountries, + }) => + CountryPhoneValidator(allowedCountries: allowedCountries); /// Gets a validator that requires the control's value pass a fixed line phone /// validation test. - static Validator get validFixedLine => - const ValidFixedLinePhoneValidator(); + static ValidPhoneValidator get validFixedLine => + const ValidPhoneValidator(type: PhoneNumberType.fixedLine); /// Gets a validator that requires the control's value pass a mobile phone /// validation test. - static Validator get validMobile => - const ValidMobilePhoneValidator(); + static ValidPhoneValidator get validMobile => + const ValidPhoneValidator(type: PhoneNumberType.mobile); } From 6c7c94d21215848194b54371b84efc7ee2410c13 Mon Sep 17 00:00:00 2001 From: Ahmed Fwela Date: Mon, 29 Apr 2024 06:08:06 +0300 Subject: [PATCH 2/3] support localization --- .../reactive_phone_form_field/CHANGELOG.md | 3 +- .../example/lib/main.dart | 30 +++++++++++++++++++ .../src/validators/validation_message.dart | 21 +++++++++++++ .../validator/country_phone_validator.dart | 2 +- .../validator/required_validator.dart | 2 +- .../validators/validator/valid_validator.dart | 4 +-- .../reactive_phone_form_field/pubspec.yaml | 2 +- 7 files changed, 58 insertions(+), 6 deletions(-) diff --git a/packages/reactive_phone_form_field/CHANGELOG.md b/packages/reactive_phone_form_field/CHANGELOG.md index 00dd091..209e649 100644 --- a/packages/reactive_phone_form_field/CHANGELOG.md +++ b/packages/reactive_phone_form_field/CHANGELOG.md @@ -1,6 +1,7 @@ -## [2.0.2] +## [3.0.0] * Support `reactive_forms: 17.x` +* Support `phone_form_field: 9.x` (read [BREAKING CHANGES](https://pub.dev/packages/phone_form_field/changelog#900)) ## [2.0.1] diff --git a/packages/reactive_phone_form_field/example/lib/main.dart b/packages/reactive_phone_form_field/example/lib/main.dart index d94f058..a4193bd 100644 --- a/packages/reactive_phone_form_field/example/lib/main.dart +++ b/packages/reactive_phone_form_field/example/lib/main.dart @@ -15,6 +15,10 @@ class MyApp extends StatelessWidget { isoCode: IsoCode.UA, nsn: '933456789', ), + validators: [ + PhoneValidators.required, + PhoneValidators.valid, + ], ), }); @@ -23,10 +27,31 @@ class MyApp extends StatelessWidget { Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Demo', + localizationsDelegates: [ + ...PhoneFieldLocalization.delegates, + ], + supportedLocales: const [ + Locale('en', ''), + Locale('es', ''), + Locale('fr', ''), + Locale('ru', ''), + Locale('uz', ''), + Locale('uk', ''), + Locale('ar', ''), + ], theme: ThemeData( primarySwatch: Colors.blue, visualDensity: VisualDensity.adaptivePlatformDensity, ), + builder: (context, child) { + return ReactiveFormConfig( + validationMessages: { + // either configure validation messages globally, or for each control + ...PhoneValidationMessage.localizedValidationMessages(context), + }, + child: child!, + ); + }, home: Scaffold( appBar: AppBar(), body: SafeArea( @@ -44,6 +69,11 @@ class MyApp extends StatelessWidget { ReactivePhoneFormField( formControlName: 'input', focusNode: FocusNode(), + // validationMessages: { + // ...PhoneValidationMessage.localizedValidationMessages( + // context, + // ), + // }, ), const SizedBox(height: 16), ElevatedButton( diff --git a/packages/reactive_phone_form_field/lib/src/validators/validation_message.dart b/packages/reactive_phone_form_field/lib/src/validators/validation_message.dart index f5a0645..b808f62 100644 --- a/packages/reactive_phone_form_field/lib/src/validators/validation_message.dart +++ b/packages/reactive_phone_form_field/lib/src/validators/validation_message.dart @@ -1,3 +1,6 @@ +import 'package:flutter/material.dart'; +import 'package:reactive_phone_form_field/reactive_phone_form_field.dart'; + class PhoneValidationMessage { static const String required = 'phone.required'; static const String invalidPhoneNumber = 'phone.invalidPhoneNumber'; @@ -6,4 +9,22 @@ class PhoneValidationMessage { 'phone.invalidMobilePhoneNumber'; static const String invalidFixedLinePhoneNumber = 'phone.invalidFixedLinePhoneNumber'; + + static Map localizedValidationMessages( + BuildContext context, + ) { + final localizations = PhoneFieldLocalization.of(context); + return { + PhoneValidationMessage.required: (error) => + localizations.requiredPhoneNumber, + PhoneValidationMessage.invalidCountry: (error) => + localizations.invalidCountry, + PhoneValidationMessage.invalidFixedLinePhoneNumber: (error) => + localizations.invalidFixedLinePhoneNumber, + PhoneValidationMessage.invalidMobilePhoneNumber: (error) => + localizations.invalidMobilePhoneNumber, + PhoneValidationMessage.invalidPhoneNumber: (error) => + localizations.invalidPhoneNumber, + }; + } } diff --git a/packages/reactive_phone_form_field/lib/src/validators/validator/country_phone_validator.dart b/packages/reactive_phone_form_field/lib/src/validators/validator/country_phone_validator.dart index e1c83be..80b8135 100644 --- a/packages/reactive_phone_form_field/lib/src/validators/validator/country_phone_validator.dart +++ b/packages/reactive_phone_form_field/lib/src/validators/validator/country_phone_validator.dart @@ -10,7 +10,7 @@ class CountryPhoneValidator extends Validator { }); @override - Map? validate(AbstractControl control) { + Map? validate(AbstractControl control) { final value = control.value; if (value == null || value.nsn.trim().isEmpty) return null; diff --git a/packages/reactive_phone_form_field/lib/src/validators/validator/required_validator.dart b/packages/reactive_phone_form_field/lib/src/validators/validator/required_validator.dart index ea84892..822301c 100644 --- a/packages/reactive_phone_form_field/lib/src/validators/validator/required_validator.dart +++ b/packages/reactive_phone_form_field/lib/src/validators/validator/required_validator.dart @@ -7,7 +7,7 @@ class RequiredPhoneValidator extends Validator { const RequiredPhoneValidator(); @override - Map? validate(AbstractControl control) { + Map? validate(AbstractControl control) { final value = control.value; if (value == null || value.nsn.trim().isEmpty) { diff --git a/packages/reactive_phone_form_field/lib/src/validators/validator/valid_validator.dart b/packages/reactive_phone_form_field/lib/src/validators/validator/valid_validator.dart index 033b6b8..93c227c 100644 --- a/packages/reactive_phone_form_field/lib/src/validators/validator/valid_validator.dart +++ b/packages/reactive_phone_form_field/lib/src/validators/validator/valid_validator.dart @@ -9,7 +9,7 @@ class ValidPhoneValidator extends Validator { final PhoneNumberType? type; @override - Map? validate(AbstractControl control) { + Map? validate(AbstractControl control) { final value = control.value; if (value == null || value.nsn.trim().isEmpty) return null; @@ -24,7 +24,7 @@ class ValidPhoneValidator extends Validator { }; return { - messageKey: type, + messageKey: type ?? true, }; } diff --git a/packages/reactive_phone_form_field/pubspec.yaml b/packages/reactive_phone_form_field/pubspec.yaml index 3355f86..f4e0b82 100644 --- a/packages/reactive_phone_form_field/pubspec.yaml +++ b/packages/reactive_phone_form_field/pubspec.yaml @@ -1,6 +1,6 @@ name: reactive_phone_form_field description: Wrapper around phone_form_field to use with reactive_forms -version: 2.0.2 +version: 3.0.0 repository: https://github.com/artflutter/reactive_forms_widgets/tree/master/packages/reactive_phone_form_field issue_tracker: https://github.com/artflutter/reactive_forms_widgets/issues From 3e7741b6196efafbf60febf3fa7e62dfb9393a04 Mon Sep 17 00:00:00 2001 From: Ahmed Fwela Date: Mon, 29 Apr 2024 12:39:25 +0300 Subject: [PATCH 3/3] simplify controller logic --- .../lib/src/reactive_phone_form_field.dart | 38 ++++++++++++------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/packages/reactive_phone_form_field/lib/src/reactive_phone_form_field.dart b/packages/reactive_phone_form_field/lib/src/reactive_phone_form_field.dart index feffdc9..102be58 100644 --- a/packages/reactive_phone_form_field/lib/src/reactive_phone_form_field.dart +++ b/packages/reactive_phone_form_field/lib/src/reactive_phone_form_field.dart @@ -22,6 +22,8 @@ export 'package:phone_form_field/phone_form_field.dart'; /// class ReactivePhoneFormField extends ReactiveFocusableFormField { + final PhoneController? _textController; + /// Creates a [ReactivePhoneFormField] that contains a [PhoneFormField]. /// /// Can optionally provide a [formControl] to bind this widget to a control. @@ -97,6 +99,7 @@ class ReactivePhoneFormField CountrySelectorNavigator countrySelectorNavigator = const CountrySelectorNavigator.page(), Function(PhoneNumber?)? onSaved, + ReactiveFormFieldCallback? onChanged, InputDecoration decoration = const InputDecoration(), TextInputType keyboardType = TextInputType.phone, TextInputAction? textInputAction, @@ -136,17 +139,17 @@ class ReactivePhoneFormField bool isCountryButtonPersistent = false, Widget Function(BuildContext, EditableTextState)? contextMenuBuilder, Function(PointerDownEvent)? onTapOutside, - }) : super( + PhoneController? controller, + }) : _textController = controller, + super( builder: (field) { final state = field as _ReactivePhoneFormFieldState; final effectiveDecoration = decoration .applyDefaults(Theme.of(state.context).inputDecorationTheme); - return PhoneFormField( countryButtonStyle: countryButtonStyle, focusNode: state.focusNode, controller: state._textController, - onChanged: field.didChange, autofillHints: autofillHints, contextMenuBuilder: contextMenuBuilder, autofocus: autofocus, @@ -159,6 +162,10 @@ class ReactivePhoneFormField ), cursorColor: cursorColor, autovalidateMode: AutovalidateMode.disabled, + onChanged: (value) { + field.didChange(value); + onChanged?.call(field.control); + }, onEditingComplete: onEditingComplete, restorationId: restorationId, keyboardType: keyboardType, @@ -210,21 +217,21 @@ class _ReactivePhoneFormFieldState @override void initState() { super.initState(); + _initializeTextController(); + } - _textController = PhoneController( - initialValue: value ?? defaultPhone, - ); + void _initializeTextController() { + final initialValue = value; + final currentWidget = widget as ReactivePhoneFormField; + _textController = (currentWidget._textController != null) + ? currentWidget._textController! + : PhoneController(initialValue: initialValue ?? defaultPhone); } @override void onControlValueChanged(dynamic value) { - if (value == null) { - _textController.value = const PhoneNumber(isoCode: IsoCode.US, nsn: ''); - } else if (value is PhoneNumber) { - _textController.value = PhoneNumber( - isoCode: value.isoCode, - nsn: value.nsn, - ); + if (value is PhoneNumber?) { + _textController.value = value ?? defaultPhone; } super.onControlValueChanged(value); @@ -232,7 +239,10 @@ class _ReactivePhoneFormFieldState @override void dispose() { - _textController.dispose(); + final currentWidget = widget as ReactivePhoneFormField; + if (currentWidget._textController == null) { + _textController.dispose(); + } super.dispose(); } }