import 'package:flutter/services.dart'; class DigitObscuringFormatter extends TextInputFormatter { @override TextEditingValue formatEditUpdate( TextEditingValue oldValue, TextEditingValue newValue) { final maskedText = maskDigits(newValue.text); return newValue.copyWith( text: maskedText, selection: updateCursorPosition(maskedText, newValue.selection)); } String maskDigits(String text) { final totalDigits = text.length; final visibleDigits = 4; final hiddenDigits = totalDigits - visibleDigits * 2; final firstVisibleDigits = text.substring(0, visibleDigits); final lastVisibleDigits = text.substring(totalDigits - visibleDigits); final maskedDigits = List.filled(hiddenDigits, '*').join(); return '$firstVisibleDigits$maskedDigits$lastVisibleDigits'; } TextSelection updateCursorPosition( String maskedText, TextSelection currentSelection) { final cursorPosition = currentSelection.baseOffset; final cursorOffset = currentSelection.extentOffset - currentSelection.baseOffset; final totalDigits = maskedText.length; const visibleDigits = 4; final hiddenDigits = totalDigits - visibleDigits * 2; final updatedPosition = cursorPosition <= visibleDigits ? cursorPosition : hiddenDigits + visibleDigits + (cursorPosition - visibleDigits); return TextSelection.collapsed( offset: updatedPosition, affinity: currentSelection.affinity); } }