Flutter: Using NumberFormat in TextInputFormatter - dart

I'm trying to use NumberFromatter in TextInputFormatter but when I try to use it, it completely messed up! This is my TextInputFormatter implementation code:
class NumericTextFormatter extends TextInputFormatter {
TextEditingValue formatEditUpdate(TextEditingValue oldValue, TextEditingValue newValue) {
if(newValue.text.length > 0) {
int num = int.parse(newValue.text.replaceAll(',', ''));
final f = new NumberFormat("#,###");
return newValue.copyWith(text: f.format(num));
} else {
return newValue.copyWith(text: '');
}
}
}
So when I add this formatter to a TextField and try to type 1 to 9, what I expect to see is something like: 123,456,789
But this is what shows in TextField:
1
12
123
1,234
12,354 <- this is where it starts
123,564
1,235,674
12,356,874 <- and it happends again
It seems that cursor moves after adding one , character. So can anyone helps me with this?

This is because after you format the value you are adding a new char but the text selection remains at the same position, one char less, this cause an expected behavior
You can modify your TextInputFormatter like this:
Fixed to support all locales and to remember cursor position
class NumericTextFormatter extends TextInputFormatter {
#override
TextEditingValue formatEditUpdate(
TextEditingValue oldValue, TextEditingValue newValue) {
if (newValue.text.isEmpty) {
return newValue.copyWith(text: '');
} else if (newValue.text.compareTo(oldValue.text) != 0) {
final int selectionIndexFromTheRight =
newValue.text.length - newValue.selection.end;
final f = NumberFormat("#,###");
final number =
int.parse(newValue.text.replaceAll(f.symbols.GROUP_SEP, ''));
final newString = f.format(number);
return TextEditingValue(
text: newString,
selection: TextSelection.collapsed(
offset: newString.length - selectionIndexFromTheRight),
);
} else {
return newValue;
}
}
}

Based on the answer and for people from Europe looking for a quick fix
class NumericTextFormatter extends TextInputFormatter {
#override
TextEditingValue formatEditUpdate(
TextEditingValue oldValue, TextEditingValue newValue) {
final currencySymbol = '€';
if (newValue.text.isEmpty || newValue.text.trim() == currencySymbol) {
return newValue.copyWith(text: '');
} else if (newValue.text.compareTo(oldValue.text) != 0) {
var selectionIndexFromTheRight =
newValue.text.length - newValue.selection.end;
final f =
NumberFormat.currency(locale: 'de', decimalDigits: 0, symbol: '');
var num = int.parse(newValue.text.replaceAll(RegExp('[^0-9]'), ''));
final newString = '$currencySymbol ' + f.format(num).trim();
return TextEditingValue(
text: newString,
selection: TextSelection.collapsed(
offset: newString.length - selectionIndexFromTheRight),
);
} else {
return newValue;
}
}
}

Related

Can I use class methods inside factory constructor via Dart

I have the below code that is creating the PriortyQueue structure using Dart. But since I cannot use heapify function inside the Constructor or factory constructor I cannot initialize PQ with an existing set of List. Can somebody guide me and show me how I can use heapify while creating PQ instance so I can initialize it with an existing List? Also If you have any other suggestions against doing something like this please also help me as well. thank you
class PriorityQueue<T extends Comparable<T>> {
List<T?> _tree;
PriorityQueue._(List<T?> tree) : _tree = tree;
factory PriorityQueue([List<T>? array]) {
List<T?> newArray = [null, ...array ?? []];
// ignore: todo
//TODO: missing heapify
return PriorityQueue._(newArray);
}
void insert(T node) {
_tree.add(node);
_swim(_tree.length - 1);
}
T getTop() {
_swap(1, _tree.length - 1);
T top = _tree.removeLast() as T;
_sink(1);
return top;
}
List<T> _heapify(List<T> array) {
int sinkNodeIndex = (array.length - 1) ~/ 2;
while (sinkNodeIndex >= 1) {
_sink(sinkNodeIndex);
sinkNodeIndex--;
}
}
void _sink(int nodeIndex) {
int leftChildIndex = nodeIndex * 2;
int rightChildIndex = leftChildIndex + 1;
int minNodeIndex = leftChildIndex;
// index can be unreachable
T? leftChild =
leftChildIndex >= _tree.length ? null : _tree[leftChildIndex];
T? rightChild =
rightChildIndex >= _tree.length ? null : _tree[rightChildIndex];
if (leftChild == null) {
return;
}
if (rightChild != null && leftChild.compareTo(rightChild) > 0) {
minNodeIndex = rightChildIndex;
}
if ((_tree[minNodeIndex] as T).compareTo(_tree[nodeIndex] as T) < 0) {
_swap(nodeIndex, minNodeIndex);
_sink(minNodeIndex);
}
}
void _swim(int nodeIndex) {
if (nodeIndex <= 1) return;
int parentIndex = nodeIndex ~/ 2;
if ((_tree[nodeIndex] as T).compareTo(_tree[parentIndex] as T) < 0) {
_swap(nodeIndex, parentIndex);
_swim(parentIndex);
}
}
void _swap(int i, int j) {
T temp = _tree[i] as T;
_tree[i] = _tree[j];
_tree[j] = temp;
}
#override
String toString() {
return _tree.toString();
}
}
I would make all the helper functions. _heapify, _sink/_swim, even _swap, be static functions which take the list as argument.
Then you can use them from anywhere, including inside the factory constructor.
Alternatively, you can change the constructor to returning:
return PriorityQueue._(newArray).._heapify();
This creates the PriorityQueue object, and then calls the _heapify method on it, before returning the value.
(I'd also make _tree have type List<T> and not insert the extra null at the beginning. It's more efficient to add/subtract 1 from indices than it is to cast to T.)
I ended up doing like Irn's first suggestion. But when I do functions static they lost Type of the class so I needed to specify for each function. Also, making List<T?> instead of List ended up with me fighting against the compiler.
class PriorityQueue<T extends Comparable<T>> {
List<T?> _tree;
PriorityQueue._(List<T?> tree) : _tree = tree;
factory PriorityQueue([List<T>? array]) {
List<T?> newArray = [null, ...array ?? []];
_heapify(newArray);
return PriorityQueue._(newArray);
}
bool get isNotEmpty {
return _tree.isNotEmpty;
}
void insert(T node) {
_tree.add(node);
_swim(_tree, _tree.length - 1);
}
void insertMultiple(List<T> array) {
for (var element in array) {
insert(element);
}
}
T? removeTop() {
if (_tree.length == 1) return null;
_swap(_tree, 1, _tree.length - 1);
T top = _tree.removeLast() as T;
_sink(_tree, 1);
return top;
}
void removeAll() {
_tree = [null];
}
static void _heapify<T extends Comparable<T>>(List<T?> array) {
int sinkNodeIndex = (array.length - 1) ~/ 2;
while (sinkNodeIndex >= 1) {
_sink(array, sinkNodeIndex);
sinkNodeIndex--;
}
}
static void _sink<T extends Comparable<T>>(List<T?> tree, int nodeIndex) {
int leftChildIndex = nodeIndex * 2;
int rightChildIndex = leftChildIndex + 1;
int minNodeIndex = leftChildIndex;
T? leftChild = leftChildIndex >= tree.length ? null : tree[leftChildIndex];
T? rightChild =
rightChildIndex >= tree.length ? null : tree[rightChildIndex];
if (leftChild == null) {
return;
}
if (rightChild != null && leftChild.compareTo(rightChild) > 0) {
minNodeIndex = rightChildIndex;
}
if ((tree[minNodeIndex] as T).compareTo(tree[nodeIndex] as T) < 0) {
_swap(tree, nodeIndex, minNodeIndex);
_sink(tree, minNodeIndex);
}
}
static void _swim<T extends Comparable<T>>(List<T?> tree, int nodeIndex) {
if (nodeIndex <= 1) return;
int parentIndex = nodeIndex ~/ 2;
if ((tree[nodeIndex] as T).compareTo(tree[parentIndex] as T) < 0) {
_swap(tree, nodeIndex, parentIndex);
_swim(tree, parentIndex);
}
}
static void _swap<T extends Comparable<T>>(List<T?> tree, int i, int j) {
T temp = tree[i] as T;
tree[i] = tree[j];
tree[j] = temp;
}
#override
String toString() {
return _tree.toString();
}
}

How to catch `IterableElementError.noElement` in Dart?

I can catch StateError and check for the message string but that looks ugly
abstract class State {}
class FoundState implements State {
final int count;
FoundState(this.count);
}
class NotFoundState implements State {
}
foo() {
final Numlist = [1, 2, 3, 4, 5];
try {
final found = Numlist.firstWhere((x) => x == 1234);
return FoundState(found);
} on StateError catch (ex) {
if (ex.message == 'No element') { // Why string? an enum would have been better
return NotFoundState();
}
}
}
main() {
final x = foo();
print(x);
}
I think about comparing with IterableElementError.noElement but it is in an internal folder.

Dart why my code not work with negative value

I try a small code but I have a strange behavior that I can't explain.
I want according to a value to return the "keyvalue" of a map which is based on the key.
My code works with positive value.
If the value is not in the array then it returns null.
It also works with negative values ​​only if the value is included in my array.
If I put a negative value lower than my array then it returns not null but zero which is false!
Keys in my map must be String.
My code that you can test on dartPad :
import 'dart:collection';
void main() {
int myVar = -360;
Map<String, dynamic> values = {
"-200" : 42,
"-100" : 21,
"0" : 0,
"100" : -22,
"150" : -30,
"200" : -43,
"300" : -64
};
Map<String, dynamic> filter(int myVar, Map<String, dynamic> values) {
SplayTreeMap<String, dynamic> newval = SplayTreeMap.of(values);
String convertString = myVar.toString();
if (values.containsKey(convertString)) {
return {convertString: values[convertString]};
}
String lowerKey;
String upperKey;
if(myVar > 0){
lowerKey = newval.lastKeyBefore(convertString);
upperKey = newval.firstKeyAfter(convertString);
}
else{
lowerKey = newval.firstKeyAfter(convertString);
upperKey = newval.lastKeyBefore(convertString);
}
print(lowerKey);
print(upperKey);
return {
if (lowerKey != null) lowerKey: values[lowerKey],
if (upperKey != null) upperKey: values[upperKey],
};
}
var result = filter(myVar, values);
print('============================');
print(result);
}
First I want to give a minor complain about the use of dynamic in the code. It is totally fine to use dynamic in cases where the type cannot be determined on runtime like JSON parsing. But in this case, all the types can be determined and the use of dynamic is not necessary. So I have fixed the code to remove the usage of dynamic and also removed unnecessary typing:
import 'dart:collection';
void main() {
const myVar = -360;
final values = {
"-200": 42,
"-100": 21,
"0": 0,
"100": -22,
"150": -30,
"200": -43,
"300": -64
};
Map<String, int> filter(int myVar, Map<String, int> values) {
final newVal = SplayTreeMap.of(values);
final convertString = myVar.toString();
if (values.containsKey(convertString)) {
return {convertString: values[convertString]};
}
String lowerKey;
String upperKey;
if (myVar > 0) {
lowerKey = newVal.lastKeyBefore(convertString);
upperKey = newVal.firstKeyAfter(convertString);
} else {
lowerKey = newVal.firstKeyAfter(convertString);
upperKey = newVal.lastKeyBefore(convertString);
}
print(lowerKey);
print(upperKey);
return {
if (lowerKey != null) lowerKey: values[lowerKey],
if (upperKey != null) upperKey: values[upperKey],
};
}
final result = filter(myVar, values);
print('============================');
print(result);
}
Your problem is that you are using SplayTreeMap to sort your keys in values but you have used Strings to represent your numbers. This is rather confusing since numbers is valid keys. But this also means that your sorting in your SplayTreeMap is alphabetical and not by number. This is properly the reason why your code does not work as expected.
You can either change the type of your keys to int or provide a compare method to your SplayTreeMap which changes how the sorting are done.
I have made the following example where I have changed the type of keys into int which makes your code work:
import 'dart:collection';
void main() {
const myVar = -360;
final values = {
-200: 42,
-100: 21,
0: 0,
100: -22,
150: -30,
200: -43,
300: -64
};
Map<int, int> filter(int myVar, Map<int, int> values) {
final newVal = SplayTreeMap.of(values);
if (values.containsKey(myVar)) {
return {myVar: values[myVar]};
}
int lowerKey;
int upperKey;
if (myVar > 0) {
lowerKey = newVal.lastKeyBefore(myVar);
upperKey = newVal.firstKeyAfter(myVar);
} else {
lowerKey = newVal.firstKeyAfter(myVar);
upperKey = newVal.lastKeyBefore(myVar);
}
print(lowerKey);
print(upperKey);
return {
if (lowerKey != null) lowerKey: values[lowerKey],
if (upperKey != null) upperKey: values[upperKey],
};
}
final result = filter(myVar, values);
print('============================');
print(result);
}
Output
-200
null
============================
{-200: 42}

How to read a file line by line in Dart

This question is a continuation of a previous question. I wrote the following piece of code to determine if File.openRead() created a Stream that could be streamed line-by-line. It turns out that the answer is no. The entire file is read and then passed to the next transform. My question is then: How do you Stream a file line-by-line in Dart?
import 'dart:async';
import 'dart:convert';
import 'dart:io';
void main(List<String> arguments) {
Stream<List<int>> stream = new File('Data.txt').openRead();
stream
.transform(const Utf8InterceptDecoder())
.transform(const LineSplitterIntercept())
.listen((line) {
// stdout.writeln(line);
}).asFuture().catchError((_) => print(_));
}
int lineSplitCount = 0;
class LineSplitterIntercept extends LineSplitter {
const LineSplitterIntercept() : super();
// Never gets called
List<String> convert(String data) {
stdout.writeln("LineSplitterIntercept.convert : Data:" + data);
return super.convert(data);
}
StringConversionSink startChunkedConversion(ChunkedConversionSink<String> sink) {
stdout.writeln("LineSplitterIntercept.startChunkedConversion Count:"+lineSplitCount.toString()+ " Sink: " + sink.toString());
lineSplitCount++;
return super.startChunkedConversion(sink);
}
}
int utfCount = 0;
class Utf8InterceptDecoder extends Utf8Decoder {
const Utf8InterceptDecoder() : super();
//never gets called
String convert(List<int> codeUnits) {
stdout.writeln("Utf8InterceptDecoder.convert : codeUnits.length:" + codeUnits.length.toString());
return super.convert(codeUnits);
}
ByteConversionSink startChunkedConversion(ChunkedConversionSink<String> sink) {
stdout.writeln("Utf8InterceptDecoder.startChunkedConversion Count:"+ utfCount.toString() + " Sink: "+ sink.toString());
utfCount++;
return super.startChunkedConversion(sink);
}
}
I think this code is useful:
import 'dart:io';
import 'dart:convert';
import 'dart:async';
main() {
final file = new File('file.txt');
Stream<List<int>> inputStream = file.openRead();
inputStream
.transform(utf8.decoder) // Decode bytes to UTF-8.
.transform(new LineSplitter()) // Convert stream to individual lines.
.listen((String line) { // Process results.
print('$line: ${line.length} bytes');
},
onDone: () { print('File is now closed.'); },
onError: (e) { print(e.toString()); });
}
If a stream is necessary, you can create it from the future that readAsLines() returns:
Stream<List<String>> stream =
new Stream.fromFuture(new File('Data.txt').readAsLines());
However it looks simpler to me to plainly process the lines one by one,
List<String> lines = new File('Data.txt').readAsLinesSync();
for (var line in lines) {
stdout.writeln(line);
}
The converter's startChunkedConversion is only called once, when the transformation is started. However, the returned sink's add method is invoked multiple times with parts of the file.
It's up to the source to decide how big the chunks are, but a 37MB file (as mentioned in your previous question) will definitely be sent in smaller chunks.
If you want to see the chunks you can either intercept startChunkedConversion and return a wrapped sink, or you can put yourself between the openRead and the transformer.
Intercept:
class InterceptSink {
static int lineSplitCount = 0;
final _sink;
InterceptSink(this._sink);
add(x) {
print("InterceptSink.add Count: $lineSplitCount");
lineSplitCount++;
_sink.add(x);
}
close() { _sink.close(); }
}
class LineSplitterIntercept extends Converter {
convert(x) { throw "unimplemented"; }
startChunkedConversion(outSink) {
var lineSink = new LineSplitter().startChunkedConversion(outSink);
return new InterceptSink(lineSink);
}
}
After openRead:
file.openRead()
.transform(UTF8.decoder)
.map(x) {
print("chunk size: ${x.length)");
return x;
}
.transform(new LineSplitter())
...
Because none of the other answers suited my situation, here is another technique:
import 'dart:io';
import 'dart:convert';
void main()
{
var file = File('/path/to/some/file.txt');
var raf = file.openSync(mode: fileMode.read);
String line;
while ((line = readLine(raf)) != null)
{
print(line);
}
}
String readLine(RandomAccessFile raf, {String lineDelimiter = '\n'}) {
var line = '';
int byte;
var priorChar = '';
var foundDelimiter = false;
while ((byte = raf.readByteSync()) != -1) {
var char = utf8.decode([byte]);
if (isLineDelimiter(priorChar, char, lineDelimiter)) {
foundDelimiter = true;
break;
}
line += char;
priorChar = char;
}
if (line.isEmpty && foundDelimiter == false) {
line = null;
}
return line;
}
bool isLineDelimiter(String priorChar, String char, String lineDelimiter) {
if (lineDelimiter.length == 1) {
return char == lineDelimiter;
} else {
return priorChar + char == lineDelimiter;
}
}
Adjusting Brett Sutton's answer for sound null safety and wider availability:
import 'dart:io';
import 'dart:convert';
bool isLineDelimiter(String priorChar, String char, String lineDelimiter)
{
if (lineDelimiter.length == 1) {
return char == lineDelimiter;
} else {
return priorChar + char == lineDelimiter;
}
}
/// Reads one line and returns its contents.
///
/// If end-of-file has been reached and the line is empty null is returned.
String? readLine(RandomAccessFile raf,
{String lineDelimiter = '\n', void Function()? onEOF}) {
String line = '';
int byte;
String priorChar = '';
byte = raf.readByteSync();
while (byte != -1) {
String char = utf8.decode([byte]);
if (isLineDelimiter(priorChar, char, lineDelimiter)) return line;
line += char;
priorChar = char;
byte = raf.readByteSync();
}
onEOF?.call();
if (line.isEmpty) return null;
return line;
}
EDIT 1:
I wanted to add some more line-specific functions I made:
/// Skips one line and returns the last byte read.
///
/// If end-of-file has been reached -1 is returned.
int skipLine(RandomAccessFile raf,
{String lineDelimiter = '\n', void Function()? onEOF}) {
int byte;
String priorChar = '';
byte = raf.readByteSync();
while (byte != -1) {
String char = utf8.decode([byte]);
if (isLineDelimiter(priorChar, char, lineDelimiter)) return byte;
priorChar = char;
byte = raf.readByteSync();
}
return byte;
}
/// Reads all lines in the file and executes [onLine] per each.
///
/// If [onLine] returns true the function terminates.
void processLines(
RandomAccessFile raf, {
String lineDelimiter = '\n',
required bool? Function(String line, bool eofReached) onLine,
}) {
bool _eofReached = false;
do {
String? _line;
_line = readLine(raf,
lineDelimiter: lineDelimiter, onEOF: () => _eofReached = true);
if (_line == null) return;
if (onLine(_line, _eofReached) == true) return;
} while (!_eofReached);
}

how to set a BasicEditField to accept dotted decimal numbers

I have added a BasicEditField to a GridFieldManager. When I test it, it allows input values like 11.11.11. How can I make my BasicEditField accept only correct double numbers, like 101.1 or 123.123. That is, allow only one decimal point.
gfm = new GridFieldManager(1, 2, 0);
gfm.add(new LabelField(" Enter value : "));
bef = new BasicEditField(BasicEditField.NO_NEWLINE|BasicEditField.FILTER_REAL_NUMERIC);
bef.setFilter(TextFilter.get(NumericTextFilter.REAL_NUMERIC));
bef.setFilter(TextFilter.get(TextFilter.REAL_NUMERIC));
bef.setText("1");
bef.setMaxSize(8);
gfm.add(bef);
add(gfm);
i had tried everything that i can. but the problem is yet in my app. can anyone give me a proper way to design a input field tha accepts decimal numbers?
Please add all the objects into the mainScreen with add(field);.
and then trying to get value of that fields.
now in your code put
String s = bef.getText();
Dialog.alert(s);
after
add(gfm);
and
To accept number like 1.1111.
then add
BasicEditField.FILTER_REAL_NUMERIC
in BasicEditFieldConstructor.
Now i think you got your solution.
finally i got the solution for a forum(forgot to copy the link)..
here it is...
inside my class i put the variables...
private int maxIntDigits = -1;
private int maxFractDigits = -1;
private String old;
i had added a BasicEditField, bef..
bef = new BasicEditField("","1");
bef.setMaxSize(8);
bef.setChangeListener(this);
add(bef);
And then in its fieldChanged().
public void fieldChanged(Field field, int context)
{
if(field==bef)
{
String str = bef.getText();
if(str.equals(""))
{
old = "";
//return;
}
if(str.indexOf('.') == str.lastIndexOf('.'))
{
if(str.indexOf('-') >= 0)
{
bef.setText(old);
}
if(validateIntPart(str) && validateFractPart(str))
{
old = str;
//return;
}
else
{
bef.setText(old);
}
}
else
{
bef.setText(old);
//return;
}
}
}
and then two functions in it...
private boolean validateIntPart(String str) {
if(maxIntDigits == -1) {
return true; //no limit has been set
}
int p = str.indexOf('.');
if(p == -1) {
p = str.length();
}
int digits = str.substring(0, p).length();
if(digits > maxIntDigits) {
return false;
} else {
return true;
}
}
private boolean validateFractPart(String str) {
if(maxFractDigits == -1) {
return true; //no limit has been set
}
int p = str.indexOf('.');
if(p == -1) {
return true; //if no '.' found then the fract part can't be too big
}
int digits = str.substring(p + 1, str.length()).length();
if(digits > maxFractDigits) {
return false;
} else {
return true;
}
}

Resources