From 587e8b976f61c33eb83476d50ceccb216ddbf102 Mon Sep 17 00:00:00 2001 From: Lukas Date: Tue, 16 Apr 2024 07:39:31 +0500 Subject: [PATCH 1/8] add recognize_country_data_by_phone.dart --- .../recognize_country_data_by_phone.dart | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 packages/flutter_libphonenumber_platform_interface/lib/src/types/recognize_country_data_by_phone.dart diff --git a/packages/flutter_libphonenumber_platform_interface/lib/src/types/recognize_country_data_by_phone.dart b/packages/flutter_libphonenumber_platform_interface/lib/src/types/recognize_country_data_by_phone.dart new file mode 100644 index 0000000..687cf09 --- /dev/null +++ b/packages/flutter_libphonenumber_platform_interface/lib/src/types/recognize_country_data_by_phone.dart @@ -0,0 +1,30 @@ +import 'package:dlibphonenumber/dlibphonenumber.dart'; +// import 'package:collection/collection.dart'; // firstWhereOrNull extension +import 'package:flutter/foundation.dart' show kDebugMode; +import 'package:flutter_libphonenumber_platform_interface/flutter_libphonenumber_platform_interface.dart'; + +/// Original method CountryWithPhoneCode.getCountryDataByPhone(phone) +/// has bad implementation because it based in substring comparison. +/// +/// For example for USA numbers it returns Bahamas, because these countries has same prefix +/// but Bahamas is alphabetically first +CountryWithPhoneCode? recognizeCountryDataByPhone(final String phone) { + // working with dlibphonenumber + final PhoneNumberUtil phoneUtil = PhoneNumberUtil.instance; + final PhoneNumber number; + try { + number = phoneUtil.parse(phone, 'ZZ'); + } on NumberParseException catch (e) { + if (kDebugMode) print('NumberParseException was thrown: ${e.toString()}'); + return null; + } + + // working with flutter_libphonenumber + final String? regionCode = phoneUtil.getRegionCodeForNumber(number); + // print('regionCode: $regionCode'); + if (regionCode != null) { + final country = CountryManager().countries.firstWhereOrNull((country) => country.countryCode == regionCode); + return country; + } + return null; +} From cb8d1810cadf89f5347dc8baf79e5bf95f40d282 Mon Sep 17 00:00:00 2001 From: Lukas Date: Tue, 16 Apr 2024 07:42:54 +0500 Subject: [PATCH 2/8] firstWhereOrNull --- .../types/recognize_country_data_by_phone.dart | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/packages/flutter_libphonenumber_platform_interface/lib/src/types/recognize_country_data_by_phone.dart b/packages/flutter_libphonenumber_platform_interface/lib/src/types/recognize_country_data_by_phone.dart index 687cf09..2e6912b 100644 --- a/packages/flutter_libphonenumber_platform_interface/lib/src/types/recognize_country_data_by_phone.dart +++ b/packages/flutter_libphonenumber_platform_interface/lib/src/types/recognize_country_data_by_phone.dart @@ -1,5 +1,5 @@ import 'package:dlibphonenumber/dlibphonenumber.dart'; -// import 'package:collection/collection.dart'; // firstWhereOrNull extension +// import 'package:collection/collection.dart'; // firstWhereOrNull extension, avoid dependency for one method import 'package:flutter/foundation.dart' show kDebugMode; import 'package:flutter_libphonenumber_platform_interface/flutter_libphonenumber_platform_interface.dart'; @@ -23,8 +23,22 @@ CountryWithPhoneCode? recognizeCountryDataByPhone(final String phone) { final String? regionCode = phoneUtil.getRegionCodeForNumber(number); // print('regionCode: $regionCode'); if (regionCode != null) { - final country = CountryManager().countries.firstWhereOrNull((country) => country.countryCode == regionCode); + final country = CountryManager().countries.firstWhereOrNull((final country) => country.countryCode == regionCode); return country; } return null; } + + +/// part of https://pub.dev/packages/collection +extension IterableExtension on Iterable { + /// The first element satisfying [test], or `null` if there are none. + T? firstWhereOrNull(final bool Function(T element) test) { + for (final element in this) { + if (test(element)) { + return element; + } + } + return null; + } +} From 75c92658133d40aa383e4e414d0d79626af0c596 Mon Sep 17 00:00:00 2001 From: Lukas Date: Tue, 16 Apr 2024 07:44:14 +0500 Subject: [PATCH 3/8] add dlibphonenumber --- packages/flutter_libphonenumber_platform_interface/pubspec.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/flutter_libphonenumber_platform_interface/pubspec.yaml b/packages/flutter_libphonenumber_platform_interface/pubspec.yaml index 843d548..b1b1151 100644 --- a/packages/flutter_libphonenumber_platform_interface/pubspec.yaml +++ b/packages/flutter_libphonenumber_platform_interface/pubspec.yaml @@ -9,6 +9,7 @@ environment: flutter: ">=3.0.0" dependencies: + dlibphonenumber: ^1.1.12 flutter: sdk: flutter plugin_platform_interface: ^2.1.4 From 43761eaeadba4d05ecedaa845486bbac5b29972b Mon Sep 17 00:00:00 2001 From: Lukas Date: Tue, 16 Apr 2024 07:44:38 +0500 Subject: [PATCH 4/8] tryRecognizePhoneWithCountry --- .../lib/src/types/input_formatter.dart | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/packages/flutter_libphonenumber_platform_interface/lib/src/types/input_formatter.dart b/packages/flutter_libphonenumber_platform_interface/lib/src/types/input_formatter.dart index 645f06f..4d933f2 100644 --- a/packages/flutter_libphonenumber_platform_interface/lib/src/types/input_formatter.dart +++ b/packages/flutter_libphonenumber_platform_interface/lib/src/types/input_formatter.dart @@ -3,6 +3,7 @@ import 'dart:math'; import 'package:flutter/services.dart'; import 'package:flutter_libphonenumber_platform_interface/flutter_libphonenumber_platform_interface.dart'; +import 'package:flutter_libphonenumber_platform_interface/src/types/recognize_country_data_by_phone.dart'; class LibPhonenumberTextFormatter extends TextInputFormatter { LibPhonenumberTextFormatter({ @@ -209,4 +210,45 @@ class LibPhonenumberTextFormatter extends TextInputFormatter { return result; } } + + /// Recognizes phone from iOS suggestions or clipboard + TextEditingValue? _tryRecognizePhoneWithCountry( + final TextEditingValue oldValue, + final TextEditingValue newValue, + ) { + // user tapped on suggested phone or pasted from clipboard + // but this not sure, so we have additional checks inside + final seemsUserTappedSuggestionOrPasted = oldValue.text.isEmpty && newValue.text.length > 1 && newValue.text.startsWith('+'); + + if (seemsUserTappedSuggestionOrPasted) { + final CountryWithPhoneCode? recognizedCountry = recognizeCountryDataByPhone(newValue.text); + if (recognizedCountry != null) { + // build mask for recognized country + final rawMask = recognizedCountry.getPhoneMask(format: phoneNumberFormat, type: phoneNumberType, removeCountryCodeFromMask: false); + // print(' mask: $rawMask'); + final mask = PhoneMask(rawMask); + + // apply mask to the input + var maskedStr = mask.apply(newValue.text); + // print('masked: $maskedStr'); + + // tell parent code what we recognized country + // it can be used for update flag or prefix in another widget + onCountryRecognized?.call(recognizedCountry); + + // remove country code from already masked string + // 2 means one for the leading + and one for the space between country code and number + if (!inputContainsCountryCode) { + maskedStr = maskedStr.substring(recognizedCountry.phoneCode.length + 2); + } + + return TextEditingValue( + text: maskedStr, + selection: TextSelection.collapsed(offset: maskedStr.length), // force cursor to the end + ); + } + } + + return null; + } } From d455c3df28db6744098b57674514d21bbffbaa7d Mon Sep 17 00:00:00 2001 From: Lukas Date: Tue, 16 Apr 2024 07:45:55 +0500 Subject: [PATCH 5/8] make inputContainsCountryCode field --- .../lib/src/types/input_formatter.dart | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/flutter_libphonenumber_platform_interface/lib/src/types/input_formatter.dart b/packages/flutter_libphonenumber_platform_interface/lib/src/types/input_formatter.dart index 4d933f2..95539f8 100644 --- a/packages/flutter_libphonenumber_platform_interface/lib/src/types/input_formatter.dart +++ b/packages/flutter_libphonenumber_platform_interface/lib/src/types/input_formatter.dart @@ -11,10 +11,7 @@ class LibPhonenumberTextFormatter extends TextInputFormatter { this.phoneNumberType = PhoneNumberType.mobile, this.phoneNumberFormat = PhoneNumberFormat.international, this.onFormatFinished, - - /// When true, mask will be applied assuming the input contains - /// a country code in it. - final bool inputContainsCountryCode = false, + this.inputContainsCountryCode = false, /// Additional digits to include this.additionalDigits = 0, @@ -53,6 +50,9 @@ class LibPhonenumberTextFormatter extends TextInputFormatter { /// Useful if you need to get the formatted value for something else to use. final FutureOr Function(String val)? onFormatFinished; + /// When true, mask will be applied assuming the input contains a country code in it. + final bool inputContainsCountryCode; + /// Allow additional digits on the end of the mask. This is useful for countries like Austria where the /// libphonenumber example number doesn't include all of the possibilities. This way we can still format /// the number but allow additional digits on the end. From f4123b222b43f58fe02b3f150986e18bfe7c218d Mon Sep 17 00:00:00 2001 From: Lukas Date: Tue, 16 Apr 2024 07:52:05 +0500 Subject: [PATCH 6/8] make inputContainsCountryCode field --- .../lib/src/types/input_formatter.dart | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/flutter_libphonenumber_platform_interface/lib/src/types/input_formatter.dart b/packages/flutter_libphonenumber_platform_interface/lib/src/types/input_formatter.dart index 95539f8..08fb680 100644 --- a/packages/flutter_libphonenumber_platform_interface/lib/src/types/input_formatter.dart +++ b/packages/flutter_libphonenumber_platform_interface/lib/src/types/input_formatter.dart @@ -11,6 +11,7 @@ class LibPhonenumberTextFormatter extends TextInputFormatter { this.phoneNumberType = PhoneNumberType.mobile, this.phoneNumberFormat = PhoneNumberFormat.international, this.onFormatFinished, + this.onCountryRecognized, this.inputContainsCountryCode = false, /// Additional digits to include @@ -50,6 +51,10 @@ class LibPhonenumberTextFormatter extends TextInputFormatter { /// Useful if you need to get the formatted value for something else to use. final FutureOr Function(String val)? onFormatFinished; + /// Optional callback that is called when it is possible to recognize the country as a result + /// of selecting a phone number from OS autofill suggestions or pasting from the clipboard + final void Function(CountryWithPhoneCode country)? onCountryRecognized; + /// When true, mask will be applied assuming the input contains a country code in it. final bool inputContainsCountryCode; From 512f02138edea8eeae60c389fdf46efbc4785824 Mon Sep 17 00:00:00 2001 From: Lukas Date: Tue, 16 Apr 2024 07:53:20 +0500 Subject: [PATCH 7/8] updates --- .../lib/src/types/input_formatter.dart | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/packages/flutter_libphonenumber_platform_interface/lib/src/types/input_formatter.dart b/packages/flutter_libphonenumber_platform_interface/lib/src/types/input_formatter.dart index 08fb680..fa3c01e 100644 --- a/packages/flutter_libphonenumber_platform_interface/lib/src/types/input_formatter.dart +++ b/packages/flutter_libphonenumber_platform_interface/lib/src/types/input_formatter.dart @@ -73,6 +73,14 @@ class LibPhonenumberTextFormatter extends TextInputFormatter { final TextEditingValue oldValue, final TextEditingValue newValue, ) { + + /// First, let's try to recognize the whole number + final recognizedValue = _tryRecognizePhoneWithCountry(oldValue, newValue); + if (recognizedValue != null) { + return recognizedValue; + } + + late final TextEditingValue result; /// Apply mask to the input From 0f896bbbebf0896351afd583cd2d974e56761c35 Mon Sep 17 00:00:00 2001 From: Andrew Coutts Date: Tue, 16 Apr 2024 09:36:17 -0400 Subject: [PATCH 8/8] Bumped pubspec version and added changelog --- packages/flutter_libphonenumber_web/CHANGELOG.md | 6 +++++- packages/flutter_libphonenumber_web/pubspec.yaml | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/flutter_libphonenumber_web/CHANGELOG.md b/packages/flutter_libphonenumber_web/CHANGELOG.md index d6659de..f613cab 100644 --- a/packages/flutter_libphonenumber_web/CHANGELOG.md +++ b/packages/flutter_libphonenumber_web/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.0.1 + +- Bump JS package. + ## 1.0.0 -* Initial implementation by laynor. +- Initial implementation by laynor. diff --git a/packages/flutter_libphonenumber_web/pubspec.yaml b/packages/flutter_libphonenumber_web/pubspec.yaml index 0482b61..66947fc 100644 --- a/packages/flutter_libphonenumber_web/pubspec.yaml +++ b/packages/flutter_libphonenumber_web/pubspec.yaml @@ -2,7 +2,7 @@ name: flutter_libphonenumber_web description: Web implementation of the flutter_libphonenumber plugin. repository: https://github.com/bottlepay/flutter_libphonenumber/tree/main/packages/flutter_libphonenumber_web issue_tracker: https://github.com/bottlepay/flutter_libphonenumber/issues -version: 1.0.0 +version: 1.0.1 environment: sdk: ">=2.19.0 <4.0.0"