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