Ben Ajax
Ben Ajax

Reputation: 740

Thousand separator without decimal separator on TextFormField flutter

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

Answers (5)

Taufik Nur Rahmanda
Taufik Nur Rahmanda

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

online59
online59

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

SirOllepopple
SirOllepopple

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

Andry Henintsoa
Andry Henintsoa

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

VadimKo
VadimKo

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

Related Questions