This seems extraordinary complicated for what it achieves but it seems to be the only solution to the problem of the cursor not being updated in Flutter text fields.
String specialChars = '00';
int length = specialChars.length;
// current cursor position, where you want to add your special characters
int cursorPos = _textController.selection.base.offset;
// the text before the point you want to add the special characters
String prefixText = _textController.text.substring(0, cursorPos);
// the text after the point ...
String suffixText = _textController.text.substring(cursorPos);
_textController.text = prefixText + specialChars + suffixText;
// set the cursor position right after the special characters
_textController.selection = TextSelection(baseOffset: cursorPos + length, extentOffset: cursorPos + length);
TextFormField(
controller: controller,
onChanged: (value) {
final formatter = NumberFormat("###,###,###.##");
final amount = formatter.parse(value);
//allow user to enter any letter at the end without clearing it.
//otherwise if the user enters "." it would get truncated by
//the formatter
if (amount == 0 || value.startsWith(formatter.format(amount.floor()))) {
return;
}
final editor = controller.value;
//only do something if the IME is not in the middle of composing
if (editor.composing.isCollapsed) {
//offset is the position of the cursor
int offset = editor.selection.extentOffset;
final pretty = formatter.format(amount);
if(offset >= value.length) {
//set the offset to the length of the new string
offset = pretty.length;
} else {
//count how many chars are in the string to the left
//of the cursor position, excluding formatting chars [,.-]
final partial = value.substring(0, offset);
for (var unit in partial.codeUnits) {
if (unit >= 44 && unit <= 46) offset--;
}
//traverse the formatted string by the same number of
//characters skipping over the formatting chars [,.-].
int i = 0;
for (var unit in pretty.codeUnits) {
if (i++ >= offset) break;
if (unit >= 44 && unit <= 46) offset++;
}
//offset is now where the new cursor position should be
}
//finally update the controller to the new offset
controller.value = editor.copyWith(
text: pretty,
selection: TextSelection.collapsed(offset: offset),
composing: TextRange.collapsed(offset),
);
}
},
)