Reputation: 740
Am using flutter_masked_text in order to format my controller to automatically add thousand separator to my currency field. Am using this to achieve that.
var controller = new MoneyMaskedTextController(decimalSeparator: '.', thousandSeparator: ',');
I don't like the way it works because it starts from 0.00
and automatically starts adding digits from the decimal section. If I type 1000
, it should become 1,000
not 1,000.00
. Is there a way I can format my controller field to add thousand separator without decimal separator?
Upvotes: 1
Views: 13632
Reputation: 1969
Here is my solution, I use my own custom TextInputFormatter
function. It also places the cursor at the correct position whenever editing the value.
import 'package:intl/intl.dart';
TextField(
...
keyboardType: TextInputType.number,
inputFormatters: [
FilteringTextInputFormatter.digitsOnly,
TextInputFormatter.withFunction((oldValue, newValue) {
final newText = NumberFormat().format(double.parse(newValue.text));
final indexFromEnd = newValue.text.length - newValue.selection.end;
final commaAfterCursor = (indexFromEnd / 3).ceil();
return newValue.copyWith(
text: newText,
selection: TextSelection.collapsed(offset: newText.length - indexFromEnd - commaAfterCursor + 1)
);
}),
],
)
Upvotes: 0
Reputation: 31
Another way without using external libraries is by creating a custom TextInputFormatter
class.
class ThousandsFormatter extends TextInputFormatter {
final NumberFormat _formatter = NumberFormat('#,###');
@override
TextEditingValue formatEditUpdate(
TextEditingValue oldValue,
TextEditingValue newValue,
) {
final formattedValue = _formatter
.format(double.tryParse(newValue.text.replaceAll(',', '')) ?? 0);
final selectionOffset = newValue.selection.baseOffset +
(formattedValue.length - newValue.text.length);
return TextEditingValue(
text: formattedValue,
selection: TextSelection.collapsed(offset: selectionOffset),
);
}
}
Then, in your TextField
widget.
TextField(
textAlign: TextAlign.right,
keyboardType: TextInputType.number,
inputFormatters: [
ThousandsFormatter(),
],
)
Upvotes: 1
Reputation: 154
I have the same problem, I found a custom input formatter code way before as a temporary solution that does the same thing, then I modified it for this specific experience. You could try this if it helps and feel free to optimize it.
class DecimalFormatter extends TextInputFormatter {
final int decimalDigits;
DecimalFormatter({this.decimalDigits = 2}) : assert(decimalDigits >= 0);
@override
TextEditingValue formatEditUpdate(TextEditingValue oldValue,
TextEditingValue newValue,) {
String newText;
if (decimalDigits == 0) {
newText = newValue.text.replaceAll(RegExp('[^0-9]'), '');
}
else {
newText = newValue.text.replaceAll(RegExp('[^0-9\.]'), '');
}
if(newText.contains('.')) {
//in case if user's first input is "."
if (newText.trim() == '.') {
return newValue.copyWith(
text: '0.',
selection: TextSelection.collapsed(offset: 2),
);
}
//in case if user tries to input multiple "."s or tries to input
//more than the decimal place
else if (
(newText.split(".").length > 2)
|| (newText.split(".")[1].length > this.decimalDigits)
) {
return oldValue;
}
else return newValue;
}
//in case if input is empty or zero
if (newText.trim() == '' || newText.trim() == '0') {
return newValue.copyWith(text: '');
}
else if (int.parse(newText) < 1) {
return newValue.copyWith(text: '');
}
double newDouble = double.parse(newText);
var selectionIndexFromTheRight =
newValue.text.length - newValue.selection.end;
String newString = NumberFormat("#,##0.##").format(newDouble);
return TextEditingValue(
text: newString,
selection: TextSelection.collapsed(
offset: newString.length - selectionIndexFromTheRight,
),
);
}
}
Upvotes: 9
Reputation: 41
I used a custom text input formatter to do something like that :
class CustomTextInputFormatter extends TextInputFormatter {
@override
TextEditingValue formatEditUpdate(
TextEditingValue oldValue, TextEditingValue newValue) {
if (newValue.text.length == 0) {
return newValue.copyWith(text: '');
} else if (newValue.text.compareTo(oldValue.text) != 0) {
int selectionIndexFromTheRight =
newValue.text.length - newValue.selection.extentOffset;
List<String> chars = newValue.text.replaceAll(' ', '').split('');
String newString = '';
for (int i = 0; i < chars.length; i++) {
if (i % 3 == 0 && i != 0) newString += ' ';
newString += chars[i];
}
return TextEditingValue(
text: newString,
selection: TextSelection.collapsed(
offset: newString.length - selectionIndexFromTheRight,
),
);
} else {
return newValue;
}
}
}
Then on your TextField:
TextField(
controller: _textController,
keyboardType: TextInputType.number,
inputFormatters: [CustomTextInputFormatter()],
)
Upvotes: 4
Reputation: 210
I never tried this package, however i can see that MoneyMaskedTextController()
has a precision
parameter.
try something like that:
var controller = new MoneyMaskedTextController(precision: 0, decimalSeparator: '.', thousandSeparator: ',');
Upvotes: 1