Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,16 @@ 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({
required this.country,
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.onCountryRecognized,
this.inputContainsCountryCode = false,

/// Additional digits to include
this.additionalDigits = 0,
Expand Down Expand Up @@ -52,6 +51,13 @@ 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;

/// 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.
Expand All @@ -67,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
Expand Down Expand Up @@ -209,4 +223,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;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import 'package:dlibphonenumber/dlibphonenumber.dart';
// 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';

/// 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((final country) => country.countryCode == regionCode);
return country;
}
return null;
}


/// part of https://pub.dev/packages/collection
extension IterableExtension<T> on Iterable<T> {
/// 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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ environment:
flutter: ">=3.0.0"

dependencies:
dlibphonenumber: ^1.1.12
flutter:
sdk: flutter
plugin_platform_interface: ^2.1.4
Expand Down
6 changes: 5 additions & 1 deletion packages/flutter_libphonenumber_web/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 1.0.1

- Bump JS package.

## 1.0.0

* Initial implementation by laynor.
- Initial implementation by laynor.
2 changes: 1 addition & 1 deletion packages/flutter_libphonenumber_web/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down