quantityFromString method
Parses numbers written with either EU or US separators. Rules:
- Rightmost of
.,
is decimal when both exist. - Single separator: 1–2 digits after => decimal.
- Single separator: exactly '000' after => decimal (so '1.000'/'1,000' => 1.0).
- Else treat separators as thousands.
Implementation
double? quantityFromString({required String regionString}) {
String s = trim();
if (s.isEmpty) return null;
// Remove spaces (incl. NBSP) and underscores used as thousand separators
s = s.replaceAll(RegExp(r'[\s\u00A0_]'), '');
// Keep only digits, sign, and separators
s = s.replaceAll(RegExp(r'[^0-9,.\-+]'), '');
if (s.isEmpty) return null;
int count(String ch) => s.split(ch).length - 1;
final hasDot = s.contains('.');
final hasComma = s.contains(',');
String? decimalSep;
if (hasDot && hasComma) {
// Decimal is whichever comes LAST
decimalSep = s.lastIndexOf('.') > s.lastIndexOf(',') ? '.' : ',';
} else if (hasDot || hasComma) {
final sep = hasDot ? '.' : ',';
final parts = s.split(sep);
final after = parts.last;
final sepCount = count(sep);
bool allZeros(String x) => RegExp(r'^0+$').hasMatch(x);
if (sepCount > 1) {
// Like 1.234.567 or 1,234,567 or 1.234.56
// If the last group length <= 2 => last is decimal, others are thousands
if (after.length <= 2) {
decimalSep = sep;
} else if (after.length == 3 && allZeros(after)) {
// 1.000 / 1,000 must be 1.0
decimalSep = sep;
} else {
// Likely all thousands groups
decimalSep = null;
}
} else {
// Only one separator in the whole string
if (after.length <= 2) {
decimalSep = sep;
} else if (after.length == 3 && allZeros(after)) {
// Explicitly make 1.000 / 1,000 => 1.0
decimalSep = sep;
} else {
decimalSep = null; // thousands
}
}
}
// Normalize to a plain "[-]digits[.digits]" shape
String normalized;
if (decimalSep != null) {
final lastIdx = s.lastIndexOf(decimalSep);
var intPart = s.substring(0, lastIdx).replaceAll(RegExp(r'[,.]'), '');
var fracPart = s.substring(lastIdx + 1).replaceAll(RegExp(r'[^0-9]'), '');
// If fractional was exactly '000', reduce to a single '0'
if (fracPart == '000') fracPart = '0';
if (intPart.isEmpty) intPart = '0';
// Keep sign if present at the very start
final sign = s.startsWith('-') ? '-' : (s.startsWith('+') ? '+' : '');
normalized = '$sign$intPart.$fracPart';
} else {
// No decimal — just remove separators as thousands and keep sign
final sign = s.startsWith('-') ? '-' : (s.startsWith('+') ? '+' : '');
final digits = s.replaceAll(RegExp(r'\D'), '');
normalized = '$sign$digits';
}
return double.tryParse(normalized);
}