formatEditUpdate method

  1. @override
TextEditingValue formatEditUpdate(
  1. TextEditingValue oldValue,
  2. TextEditingValue newValue
)
override

Called when text is being typed or cut/copy/pasted in the EditableText.

You can override the resulting text based on the previous text value and the incoming new text value.

When formatters are chained, oldValue reflects the initial value of TextEditingValue at the beginning of the chain.

Implementation

@override
TextEditingValue formatEditUpdate(
  final TextEditingValue oldValue,
  final TextEditingValue newValue,
) {
  // print('old: ${oldValue.toJSON()}');
  // print('new: ${newValue.toJSON()}');

  int signals = RegExp('-').allMatches(newValue.text).length;

  bool isNegative = oldValue.text.startsWith('-');
  bool delNegative = false;

  if (isNegative && signals != 1) {
    isNegative = false;
    delNegative = true;
  }

  if (!isNegative && signals == 1) {
    isNegative = true;
  }

  String newText = newValue.text.replaceAll('-', '');

  /// Empty value
  if (newText.isEmpty ||
      newText == decimalSeparator ||
      newText == thousandSeparator) {
    // print('New value is empty or equals to "$decimalSeparator" '
    //       'or equals to "$thousandSeparator"');
    // print('\n');

    return TextEditingValue(
      text: '${isNegative ? '-0' : '0'}$decimalSeparator'.padRight(
        precision + (isNegative ? 3 : 2),
        '0',
      ),
      selection: TextSelection.collapsed(offset: (isNegative ? 2 : 1)),
    );
  }

  /// Char not allowed.
  if (allow.allMatches(newText).length != newText.length) {
    // print('Char not allowed.');
    // print('\n');

    return oldValue;
  }

  int oldDecimalCount = oldValue.text.split(decimalSeparator).length;
  int oldThousandCount = oldValue.text.split(thousandSeparator).length;

  int newDecimalCount = newText.split(decimalSeparator).length;
  int newThousandCount = newText.split(thousandSeparator).length;

  if (oldDecimalCount < newDecimalCount ||
      oldThousandCount < newThousandCount) {
    int curPos = oldValue.text.indexOf(decimalSeparator) + 1;

    // print('curPos: $curPos');

    if (newValue.selection.baseOffset <= curPos) {
      Map<String, dynamic> oldValueJson = oldValue.toJSON();
      oldValueJson['selectionBase'] = curPos;
      oldValueJson['selectionExtent'] = curPos;

      // print('\n');

      return TextEditingValue.fromJSON(oldValueJson);
    }

    // print('\n');

    return oldValue;
  }

  /// Decimal Part
  if (newText.contains(decimalSeparator)) {
    List<String> parts = newText.split(decimalSeparator);

    // print('Integer Part: ${parts.first}');
    // print('Decimal Part: ${parts.last}');

    String decimalPart = parts.last;

    decimalPart = decimalPart.length > precision
        ? decimalPart.substring(0, precision)
        : decimalPart.padRight(precision, '0');

    newText = '${parts.first}$decimalSeparator$decimalPart';
  } else {
    if (newText.length == 1) {
      newText += decimalSeparator.padRight(precision + 1, '0');
    } else {
      int pos = newText.length - precision;
      newText =
          newText.substring(0, pos) +
          decimalSeparator +
          newText.substring(pos);
    }
  }

  int firstLength = newText.length;

  /// Integer Part
  List<String> parts = newText.split(decimalSeparator);

  int integerPart =
      int.tryParse(parts.first.replaceAll(thousandSeparator, '')) ?? 0;

  List<String> numbers = integerPart.toString().split('').reversed.toList();

  for (int pos = 3; pos < numbers.length; pos += 4) {
    numbers.insert(pos, thousandSeparator);
  }

  newText = numbers.reversed.join() + decimalSeparator + parts.last;

  /// Cursor Positioning
  int newTextLength = newText.length;

  int newPos = newValue.selection.baseOffset;

  // print('newPos: $newPos');

  newPos += newTextLength - firstLength;

  // print('delta: ${newTextLength - firstLength}');

  if (delNegative) {
    newPos -= 2;
  }

  if (newPos < 1) {
    newPos = 0;
  }

  newText = (isNegative ? '-' : '') + newText;

  if (newPos > newText.length) {
    newPos = newText.length;
  }

  Map<String, dynamic> newValueJson = newValue.toJSON();
  newValueJson['text'] = newText;
  newValueJson['selectionBase'] = newPos;
  newValueJson['selectionExtent'] = newPos;

  // print('newValueJson: $newValueJson');
  // print('\n');

  return TextEditingValue.fromJSON(newValueJson);
}