@@ -4,6 +4,12 @@ import 'package:flutter/material.dart';
44import 'package:flutter/services.dart' ;
55
66class MaskTextInputFormatter implements TextInputFormatter {
7+ /// Set the autocompletion behavior:
8+ /// - [MaskAutoCompletion.lazy] (default): autocomplete unfiltered characters once the following filtered character is input.
9+ /// For example, with the mask "#/#" and the sequence of characters "1" then "2", the formatter will output "1", then "1/2"
10+ /// - [MaskAutoCompletion.eager] : autocomplete unfiltered characters when the previous filtered character is input.
11+ /// For example, with the mask "#/#" and the sequence of characters "1" then "2", the formatter will output "1/", then "1/2"
12+ final MaskAutoCompletion autoCompletion;
713
814 String ? _mask;
915 List <String > _maskChars = [];
@@ -23,7 +29,8 @@ class MaskTextInputFormatter implements TextInputFormatter {
2329 MaskTextInputFormatter ({
2430 String ? mask,
2531 Map <String , RegExp >? filter,
26- String ? initialText
32+ String ? initialText,
33+ this .autoCompletion = MaskAutoCompletion .lazy,
2734 }) {
2835 updateMask (mask: mask, filter: filter ?? {"#" : RegExp (r'[0-9]' ), "A" : RegExp (r'[^0-9]' )});
2936 if (initialText != null ) {
@@ -32,7 +39,7 @@ class MaskTextInputFormatter implements TextInputFormatter {
3239 }
3340
3441 /// Change the mask
35- TextEditingValue updateMask ({ String ? mask, Map <String , RegExp >? filter}) {
42+ TextEditingValue updateMask ({String ? mask, Map <String , RegExp >? filter}) {
3643 _mask = mask;
3744 if (filter != null ) {
3845 _updateFilter (filter);
@@ -74,17 +81,17 @@ class MaskTextInputFormatter implements TextInputFormatter {
7481
7582 /// Mask some text
7683 String maskText (String text) {
77- return MaskTextInputFormatter (mask: _mask, filter: _maskFilter, initialText: text).getMaskedText ();
84+ return MaskTextInputFormatter (mask: _mask, filter: _maskFilter, initialText: text, autoCompletion : autoCompletion ).getMaskedText ();
7885 }
7986
8087 /// Unmask some text
8188 String unmaskText (String text) {
82- return MaskTextInputFormatter (mask: _mask, filter: _maskFilter, initialText: text).getUnmaskedText ();
89+ return MaskTextInputFormatter (mask: _mask, filter: _maskFilter, initialText: text, autoCompletion : autoCompletion ).getUnmaskedText ();
8390 }
8491
8592 @override
8693 TextEditingValue formatEditUpdate (TextEditingValue oldValue, TextEditingValue newValue) {
87- if (_lastResValue == oldValue && newValue == _lastNewValue) {
94+ if (_lastResValue == oldValue && newValue == _lastNewValue && autoCompletion != MaskAutoCompletion .eager ) {
8895 return oldValue;
8996 }
9097 if (oldValue.text.isEmpty) {
@@ -105,12 +112,21 @@ class MaskTextInputFormatter implements TextInputFormatter {
105112
106113 final String beforeText = oldValue.text;
107114 final String afterText = newValue.text;
115+ final bool isDeletion = afterText.length < beforeText.length;
108116
109117 final TextSelection beforeSelection = oldValue.selection;
110118 final TextSelection afterSelection = newValue.selection;
111119
112- final int beforeSelectionStart = afterSelection.isValid ? beforeSelection.isValid ? beforeSelection.start : 0 : 0 ;
113- final int beforeSelectionLength = afterSelection.isValid ? beforeSelection.isValid ? beforeSelection.end - beforeSelection.start : 0 : oldValue.text.length;
120+ final int beforeSelectionStart = afterSelection.isValid
121+ ? beforeSelection.isValid
122+ ? beforeSelection.start
123+ : 0
124+ : 0 ;
125+ final int beforeSelectionLength = afterSelection.isValid
126+ ? beforeSelection.isValid
127+ ? beforeSelection.end - beforeSelection.start
128+ : 0
129+ : oldValue.text.length;
114130
115131 final int lengthDifference = afterText.length - (beforeText.length - beforeSelectionLength);
116132 final int lengthRemoved = lengthDifference < 0 ? lengthDifference.abs () : 0 ;
@@ -152,7 +168,7 @@ class MaskTextInputFormatter implements TextInputFormatter {
152168 targetCursorPosition += replacementText.length;
153169 }
154170
155- if (beforeResultTextLength == 0 && _resultTextArray.length > 1 ) {
171+ if (beforeResultTextLength == 0 && _resultTextArray.length > 1 ) {
156172 for (var i = 0 ; i < mask.length; i++ ) {
157173 if (_maskChars.contains (mask[i])) {
158174 final resultPrefix = _resultTextArray._symbolArray.take (i).toList ();
@@ -207,7 +223,7 @@ class MaskTextInputFormatter implements TextInputFormatter {
207223 cursorPos = maskPos;
208224 }
209225
210- if (! curTextInRange) {
226+ if (_mustEndMaskIteration ( curTextInRange, isMaskChar) ) {
211227 break ;
212228 } else {
213229 _resultTextMasked += mask[maskPos];
@@ -220,8 +236,17 @@ class MaskTextInputFormatter implements TextInputFormatter {
220236 }
221237
222238 if (nonMaskedCount > 0 ) {
223- _resultTextMasked = _resultTextMasked.substring (0 , _resultTextMasked.length - nonMaskedCount);
224- cursorPos -= nonMaskedCount;
239+ switch (autoCompletion) {
240+ case MaskAutoCompletion .eager:
241+ if (! isDeletion) {
242+ cursorPos += nonMaskedCount;
243+ }
244+ break ;
245+ case MaskAutoCompletion .lazy:
246+ _resultTextMasked = _resultTextMasked.substring (0 , _resultTextMasked.length - nonMaskedCount);
247+ cursorPos -= nonMaskedCount;
248+ break ;
249+ }
225250 }
226251
227252 if (_resultTextArray.length > _maskLength) {
@@ -236,11 +261,20 @@ class MaskTextInputFormatter implements TextInputFormatter {
236261 baseOffset: finalCursorPosition,
237262 extentOffset: finalCursorPosition,
238263 affinity: newValue.selection.affinity,
239- isDirectional: newValue.selection.isDirectional
240- )
264+ isDirectional: newValue.selection.isDirectional,
265+ ),
241266 );
242267 }
243268
269+ bool _mustEndMaskIteration (bool curTextInRange, bool isMaskChar) {
270+ switch (autoCompletion) {
271+ case MaskAutoCompletion .lazy:
272+ return ! curTextInRange;
273+ case MaskAutoCompletion .eager:
274+ return isMaskChar;
275+ }
276+ }
277+
244278 void _calcMaskLength () {
245279 _maskLength = 0 ;
246280 final mask = _mask;
@@ -260,7 +294,6 @@ class MaskTextInputFormatter implements TextInputFormatter {
260294}
261295
262296class _TextMatcher {
263-
264297 final List <String > _symbolArray = < String > [];
265298
266299 int get length => _symbolArray.fold (0 , (prev, match) => prev + match.length);
@@ -277,7 +310,7 @@ class _TextMatcher {
277310
278311 void removeAt (int index) => _symbolArray.removeAt (index);
279312
280- String operator [](int index) => _symbolArray[index];
313+ String operator [](int index) => _symbolArray[index];
281314
282315 void clear () => _symbolArray.clear ();
283316
@@ -290,5 +323,9 @@ class _TextMatcher {
290323 _symbolArray.add (text[i]);
291324 }
292325 }
326+ }
293327
328+ enum MaskAutoCompletion {
329+ lazy,
330+ eager,
294331}
0 commit comments