Line data Source code
1 : import 'package:flutter/foundation.dart'; 2 : import 'package:flutter/services.dart'; 3 : 4 : /// 5 : /// 6 : /// 7 : class DecimalTextFormatter extends TextInputFormatter { 8 : final int precision; 9 : final String decimalSeparator; 10 : final String thousandSeparator; 11 : final RegExp allow; 12 : 13 : /// 14 : /// 15 : /// 16 1 : DecimalTextFormatter({ 17 : required this.precision, 18 : this.decimalSeparator = ',', 19 : this.thousandSeparator = '.', 20 2 : }) : allow = RegExp('[0-9$decimalSeparator$thousandSeparator]'); 21 : 22 : /// 23 : /// 24 : /// 25 0 : @override 26 : TextEditingValue formatEditUpdate( 27 : TextEditingValue oldValue, 28 : TextEditingValue newValue, 29 : ) { 30 : if (kDebugMode) { 31 0 : print('old: ${oldValue.toJSON()}'); 32 0 : print('new: ${newValue.toJSON()}'); 33 : } 34 : 35 0 : String newText = newValue.text; 36 : 37 : /// Empty value 38 0 : if (newText.isEmpty || 39 0 : newText == decimalSeparator || 40 0 : newText == thousandSeparator) { 41 : if (kDebugMode) { 42 0 : print('New value is empty or equals to "$decimalSeparator" ' 43 0 : 'or equals to "$thousandSeparator"'); 44 0 : print('\n'); 45 : } 46 : 47 0 : return TextEditingValue( 48 0 : text: '0$decimalSeparator'.padRight(precision + 2, '0'), 49 : selection: const TextSelection.collapsed(offset: 1), 50 : ); 51 : } 52 : 53 : /// Char not allowed. 54 0 : if (allow.allMatches(newText).length != newText.length) { 55 : if (kDebugMode) { 56 0 : print('Char not allowed.'); 57 0 : print('\n'); 58 : } 59 : 60 : return oldValue; 61 : } 62 : 63 0 : int oldDecimalCount = oldValue.text.split(decimalSeparator).length; 64 0 : int oldThousandCount = oldValue.text.split(thousandSeparator).length; 65 : 66 0 : int newDecimalCount = newText.split(decimalSeparator).length; 67 0 : int newThousandCount = newText.split(thousandSeparator).length; 68 : 69 0 : if (oldDecimalCount < newDecimalCount || 70 0 : oldThousandCount < newThousandCount) { 71 0 : int curPos = oldValue.text.indexOf(decimalSeparator) + 1; 72 : 73 : if (kDebugMode) { 74 0 : print('curPos: $curPos'); 75 : } 76 : 77 0 : if (newValue.selection.baseOffset <= curPos) { 78 0 : Map<String, dynamic> oldValueJson = oldValue.toJSON(); 79 0 : oldValueJson['selectionBase'] = curPos; 80 0 : oldValueJson['selectionExtent'] = curPos; 81 : 82 : if (kDebugMode) { 83 0 : print('\n'); 84 : } 85 : 86 0 : return TextEditingValue.fromJSON(oldValueJson); 87 : } 88 : 89 : if (kDebugMode) { 90 0 : print('\n'); 91 : } 92 : 93 : return oldValue; 94 : } 95 : 96 : /// Decimal Part 97 0 : if (newText.contains(decimalSeparator)) { 98 0 : List<String> parts = newText.split(decimalSeparator); 99 : 100 : if (kDebugMode) { 101 0 : print('Integer Part: ${parts.first}'); 102 0 : print('Decimal Part: ${parts.last}'); 103 : } 104 : 105 0 : String decimalPart = parts.last; 106 : 107 0 : if (decimalPart.length > precision) { 108 0 : decimalPart = decimalPart.substring(0, precision); 109 : } else { 110 : if (kDebugMode) { 111 0 : print('decimalPart Length: ${decimalPart.length}'); 112 : } 113 : 114 0 : decimalPart = decimalPart.padRight(precision, '0'); 115 : } 116 : 117 0 : newText = '${parts.first}$decimalSeparator$decimalPart'; 118 : } else { 119 0 : if (newText.length == 1) { 120 0 : newText += decimalSeparator.padRight(precision + 1, '0'); 121 : } else { 122 0 : int pos = newText.length - precision; 123 0 : newText = newText.substring(0, pos) + 124 0 : decimalSeparator + 125 0 : newText.substring(pos); 126 : } 127 : } 128 : 129 0 : int firstLength = newText.length; 130 : 131 : /// Integer Part 132 0 : List<String> parts = newText.split(decimalSeparator); 133 : 134 : int integerPart = 135 0 : int.tryParse(parts.first.replaceAll(thousandSeparator, '')) ?? 0; 136 : 137 0 : List<String> numbers = integerPart.toString().split('').reversed.toList(); 138 : 139 0 : for (int pos = 3; pos < numbers.length; pos += 4) { 140 0 : numbers.insert(pos, thousandSeparator); 141 : } 142 : 143 0 : newText = numbers.reversed.join() + decimalSeparator + parts.last; 144 : 145 0 : Map<String, dynamic> newValueJson = newValue.toJSON(); 146 : 147 0 : newValueJson['text'] = newText; 148 : 149 : /// Cursor Positioning 150 0 : int newTextLength = newText.length; 151 : 152 0 : int newPos = newValue.selection.baseOffset; 153 : 154 0 : int delta = newTextLength - firstLength; 155 : 156 : if (kDebugMode) { 157 0 : print('delta: $delta'); 158 : } 159 : 160 0 : newPos += delta; 161 : 162 0 : if (newValue.selection.baseOffset > newTextLength) { 163 : newPos = newTextLength; 164 : } 165 : 166 0 : if (newPos < 1) { 167 : newPos = 0; 168 : } 169 : 170 0 : newValueJson['selectionBase'] = newPos; 171 0 : newValueJson['selectionExtent'] = newPos; 172 : 173 : if (kDebugMode) { 174 0 : print('newValueJson: $newValueJson'); 175 0 : print('\n'); 176 : } 177 : 178 0 : return TextEditingValue.fromJSON(newValueJson); 179 : } 180 : }