formatNumber function
Formats a num value with locale-aware grouping and decimal separators in pure Dart.
Formats integers and floating point numbers according to language conventions,
using appropriate thousand separators (",", ".", " ", or "٬") and decimal
points (".", ",", or "٫"). When locale is omitted, uses the active
BloomI18n.instance.locale signal value.
Uses package:intl locale data, including locale-specific grouping and
numbering patterns. An unrecognized locale falls back to Bloom's small
built-in formatter.
formatNumber(1234567.89, locale: 'en-US'); // "1,234,567.89"
formatNumber(1234567.89, locale: 'de-DE'); // "1.234.567,89"
formatNumber(1234567.89, locale: 'fr-FR'); // "1 234 567,89"
See also:
- formatPercent, for formatting percentage values.
- formatCurrency, for currency formatting.
- localizedNumber, convenience top-level shorthand.
Implementation
String formatNumber(
num value, {
String? locale,
int? decimalDigits,
bool useGrouping = true,
String style = 'decimal',
}) {
final intlLocale =
(locale ?? BloomI18n.instance.locale.value).replaceAll('-', '_');
try {
final formatter = intl.NumberFormat.decimalPatternDigits(
locale: intlLocale, decimalDigits: decimalDigits);
if (!useGrouping) formatter.turnOffGrouping();
return formatter.format(value);
} catch (_) {
// Preserve the lightweight fallback for a locale absent from intl data.
}
final loc = (locale ?? BloomI18n.instance.locale.value)
.replaceAll('_', '-')
.toLowerCase();
final lang = loc.split('-').first;
// Determine separators
String decimalSep = '.';
String groupSep = ',';
if (['de', 'it', 'es', 'pt', 'nl', 'tr', 'id'].contains(lang)) {
decimalSep = ',';
groupSep = '.';
} else if (['fr', 'ru', 'sv', 'pl', 'cs', 'fi', 'no', 'uk', 'bg']
.contains(lang)) {
decimalSep = ',';
groupSep = ' ';
} else if (['ar'].contains(lang)) {
decimalSep = '٫';
groupSep = '٬';
}
// Handle decimals
final isInt = value is int || value == value.roundToDouble();
String formatted;
if (decimalDigits != null) {
formatted = value.toStringAsFixed(decimalDigits);
} else if (isInt) {
formatted = value.toInt().toString();
} else {
formatted = value.toString();
}
final parts = formatted.split('.');
String integerPart = parts[0];
final isNegative = integerPart.startsWith('-');
if (isNegative) integerPart = integerPart.substring(1);
if (useGrouping && integerPart.length > 3) {
final buf = StringBuffer();
final len = integerPart.length;
for (int i = 0; i < len; i++) {
if (i > 0 && (len - i) % 3 == 0) {
buf.write(groupSep);
}
buf.write(integerPart[i]);
}
integerPart = buf.toString();
}
if (isNegative) integerPart = '-$integerPart';
if (parts.length > 1 && parts[1].isNotEmpty) {
return '$integerPart$decimalSep${parts[1]}';
}
return integerPart;
}