import 'package:flutter/services.dart'; class PhoneInputFormatter extends TextInputFormatter { @override TextEditingValue formatEditUpdate( TextEditingValue oldValue, TextEditingValue newValue, ) { // Если ввод пустой — возвращаем его как есть if (newValue.text.isEmpty) { return newValue; } // Убираем все нецифровые символы из ввода String input = newValue.text.replaceAll(RegExp(r'[^0-9]'), ''); // Ограничиваем длину — максимум 11 цифр (без +7) if (input.length > 11) { input = input.substring(0, 11); } // Применяем маску String formatted = _applyPhoneMask(input); // Возвращаем отформатированное значение, сохраняя курсор return newValue.copyWith( text: formatted, selection: TextSelection.collapsed( offset: _getCursorOffset(input, formatted), ), ); } // Применяет маску +7 (XXX) XXX-XX-XX String _applyPhoneMask(String digits) { if (digits.isEmpty) return ''; if (digits.length == 1) return '+7'; if (digits.length == 2) return '+7 (${'$digits'.substring(1, 2)})'; if (digits.length == 3) return '+7 (${digits.substring(1)})'; if (digits.length == 4) return '+7 (${digits.substring(1, 4)})'; if (digits.length == 5) return '+7 (${digits.substring(1, 4)}) ${digits.substring(4, 5)}'; if (digits.length == 6) return '+7 (${digits.substring(1, 4)}) ${digits.substring(4, 6)}'; if (digits.length == 7) return '+7 (${digits.substring(1, 4)}) ${digits.substring(4, 7)}'; if (digits.length == 8) return '+7 (${digits.substring(1, 4)}) ${digits.substring(4, 7)}-${digits.substring(7, 8)}'; if (digits.length == 9) return '+7 (${digits.substring(1, 4)}) ${digits.substring(4, 7)}-${digits.substring(7, 9)}'; if (digits.length == 10) return '+7 (${digits.substring(1, 4)}) ${digits.substring(4, 7)}-${digits.substring(7, 9)}-${digits.substring(9, 10)}'; if (digits.length == 11) return '+7 (${digits.substring(1, 4)}) ${digits.substring(4, 7)}-${digits.substring(7, 9)}-${digits.substring(9, 11)}'; return digits; } // Вычисляет новую позицию курсора после форматирования int _getCursorOffset(String oldDigits, String formatted) { // Если ввод был короче — курсор должен быть в конце if (oldDigits.length >= formatted.length) { return formatted.length; } // Сопоставляем позиции: сколько цифр было до текущей позиции int cursorPosition = 0; for (int i = 0; i < oldDigits.length; i++) { String before = _applyPhoneMask(oldDigits.substring(0, i + 1)); String after = _applyPhoneMask(oldDigits.substring(0, i + 2)); if (after.length > before.length) { cursorPosition++; } } return cursorPosition; } }