Маски ввода номера телефона на Flutter
Всем привет. Нужно поговорить немного про такую стандартную и обыденную часть любого приложения, как ввод номера телефона пользователя. Речь только о российских телефонах. Казалось бы, что может быть проще? Ставим в начало строки + или даже +7 и пользователь указывает телефон начиная с 9. Для удобства можно накинуть и маску и казалось бы все хорошо. Но нет иногда пользователи длоб… смотрят не туда и пишут с 8 или с 7 и все. Без авторизации пользователь не может использовать сервис и дать нам денег на дошик на смузи. Посмотрим несколько примеров из других сервисов:
Как это работает в wildberries
Логика проста: ставим +, а дальше пользователь сам поймет что вводить.
Как это работает в Тинькофф
Задача
Мы делаем пакет для пользователей из России и пары случайных иностранцев. Наши требования:
Поле по умолчанию пустое. Пользователь сам решает что и как вводить
при вводе 7,8,9 должна работать маска российских номеров (Х (ХХХ) ХХХ-ХХ-ХХ),
в маску можно будет ввести и иностранный номер. Например +1
Время стучать по Клавиатуре
Для работы будем использовать наследника TextInputFormatter. Его можно добавить в любое текстовое поле в inputFormatters.А потом вытащить всю нужную нам информацию.
class RuPhoneInputFormatter extends TextInputFormatter{
@override
TextEditingValue formatEditUpdate(TextEditingValue oldValue, TextEditingValue newValue) {
}
}
Для работы нам нужны
переменная для форматированного значения
флаг российски ли это номер
метод форматирующий номер
геттер чистого номера (номер без форматирования. Для российских начинается с 9, для иностранных c первой цифры в текстовом поле)
Добавляем нужные переменные и дефолтные значения (вдруг мы номер уже знаем)
class RuPhoneInputFormatter extends TextInputFormatter{
//форматированный телефон
String _formattedPhone = "";
//Российский ли номер
bool _isRu=false;
//Добавляем возможность указать номер по дефолту
RuPhoneInputFormatter({
String? initialText,
}) {
if (initialText != null) {
formatEditUpdate(
TextEditingValue.empty, TextEditingValue(text: initialText));
}
}
///Иетод возвращает форматированнный телефон
String getMaskedPhone() {
return _formattedPhone;
}
///возвращает чистый телефон. для России начинается с 9
String getClearPhone() {
if(_formattedPhone.isEmpty){
return '';
}
if(!_isRu){
return _formattedPhone.replaceAll(RegExp(r'\D'), '');
}
return _formattedPhone.replaceAll(RegExp(r'\D'), '').substring(
1,
(_formattedPhone.replaceAll(RegExp(r'\D'), '').length >= 11)
? 11
: _formattedPhone.replaceAll(RegExp(r'\D'), '').length);
}
///Проверяет заполнил ли пользователь телефон. Актуально только для Российских телефонов
bool isDone(){
if(!_isRu){
return true;
}
return (_formattedPhone.replaceAll(RegExp(r'\D'), '').length>10);
}
///возвращает флаг Российски ли номер
get isRussian=>_isRu;
}
Пишем метод для форматирования
метод должен проверять первые цифры и форматировать только если это 7,8,9
String _formattingPhone(String text){
//регулярка протиа букв. в телефоне только цифры
text=text.replaceAll(RegExp(r'\D'), '');
if(text.isNotEmpty){
String phone='';
//проверяем российски ли номер
if(['7','8','9'].contains(text[0])){
_isRu=true;
//если пользователь начал с 9, то добавим 7
if(text[0]=='9'){
text='7$text';
}
//Проверяем нужен ли +
String firstSymbols=(text[0]=='8') ? '8':'+7';
//само форматирование
phone='$firstSymbols ';
if(text.length>1){
phone+='(${text.substring(1,(text.length<4)?text.length:4)}';
}if(text.length>=5){
phone+=') ${text.substring(4,(text.length<7)?text.length:7)}';
}
if(text.length>=8){
phone+='-${text.substring(7,(text.length<9)?text.length:9)}';
}
if(text.length>=10){
phone+='-${text.substring(9,(text.length<11)?text.length:11)}';
}
return phone;
}else{
_isRu=false;
return '+$text';
}
}
return '';
}
Собираем все в кучу
Добавим код для удобства редактирования телефона
@override
TextEditingValue formatEditUpdate(TextEditingValue oldValue, TextEditingValue newValue) {
String text=newValue.text.replaceAll(RegExp(r'\D'), '');
int selectionStart=oldValue.selection.end;
//проверяем стерает ли пользователь все символы?
if(oldValue.text=='${newValue.text} '){
_formattedPhone='';
return TextEditingValue(
text: _formattedPhone,
selection: TextSelection(
baseOffset: _formattedPhone.length,
extentOffset: _formattedPhone.length,
affinity: newValue.selection.affinity,
isDirectional: newValue.selection.isDirectional
)
);
}
//проверяем редактирует ли пользователь телефон где то по середине?
if(selectionStart!=_formattedPhone.length){
_formattedPhone= _formattingPhone(text);
//если да, то не перекидываем курсов в самый конец
return TextEditingValue(
text: _formattedPhone,
selection: TextSelection(
baseOffset: newValue.selection.baseOffset,
extentOffset: newValue.selection.extentOffset,
affinity: newValue.selection.affinity,
isDirectional: newValue.selection.isDirectional
)
);
}
_formattedPhone= _formattingPhone(text);
//если пользователь просто вводит телефон,
//то переносим курсор в конец форматированной строки
return TextEditingValue(
text: _formattedPhone,
selection: TextSelection(
baseOffset: _formattedPhone.length,
extentOffset: _formattedPhone.length,
affinity: newValue.selection.affinity,
isDirectional: newValue.selection.isDirectional
)
);
}
Итоги
пример работы виджета. пользователь полностью свободен при вводе телефона. Так же сохранилась поддержка и иностранных телефонов.
А что дальше?
в планах добавить маски всех телефонов стран СНГ.
Пакет
Для удобства все исходники тут
А еще есть пакет на pub.dev тут
Поддержать автора тут
Вдохновение черпал в видосике на Ютубе