1

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?

5 Answers 5

9

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,
        ),
      );
    }
}
Sign up to request clarification or add additional context in comments.

1 Comment

This works. However, there's an issue. If i try to extract the string from the form field using the controller and parse it into a double, the commas come along with it, so it keeps giving me an exception.
4

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()],
)

Comments

1

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: ',');

Comments

1

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(),
    ],
  )

Comments

1

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)
      );
    }),
  ],
)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.