number_format.dart 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  1. import 'package:flutter/material.dart';
  2. import '../theme/app_colors.dart';
  3. /// 显示 WS 原始价格字符串:整数部分加千分符,小数部分去掉尾零
  4. /// 例: "79126.31000000" → "79,126.31" | "0.384200" → "0.3842"
  5. String formatRawPrice(String priceStr) {
  6. if (priceStr.isEmpty) return '--';
  7. final parts = priceStr.split('.');
  8. final intPart = parts[0].replaceAllMapped(
  9. RegExp(r'(\d)(?=(\d{3})+$)'),
  10. (m) => '${m[1]},',
  11. );
  12. if (parts.length > 1) {
  13. final decimal = parts[1].replaceAll(RegExp(r'0+$'), '');
  14. if (decimal.isEmpty) return intPart;
  15. return '$intPart.$decimal';
  16. }
  17. return intPart;
  18. }
  19. /// 价格格式化 — 自动判断小数位
  20. /// BTC: 67,234.56 | DOGE: 0.123456
  21. String formatPrice(double price, {int? decimalPlaces}) {
  22. if (price == 0) return '0.00';
  23. final decimals = decimalPlaces ?? _autoDecimals(price);
  24. final parts = price.toStringAsFixed(decimals).split('.');
  25. final intPart = parts[0].replaceAllMapped(
  26. RegExp(r'(\d)(?=(\d{3})+$)'),
  27. (m) => '${m[1]},',
  28. );
  29. return parts.length > 1 ? '$intPart.${parts[1]}' : intPart;
  30. }
  31. int _autoDecimals(double price) {
  32. if (price >= 1000) return 2;
  33. if (price >= 1) return 4;
  34. return 6;
  35. }
  36. /// 涨跌幅格式化
  37. /// +2.34% | -1.20% | 0.00%
  38. String formatChange(double change) {
  39. final sign = change >= 0 ? '+' : '';
  40. return '$sign${change.toStringAsFixed(2)}%';
  41. }
  42. double _pow10(int n) {
  43. double r = 1;
  44. for (var i = 0; i < n; i++) { r *= 10; }
  45. return r;
  46. }
  47. /// 数量/金额格式化:截断到 [decimals] 位小数(不四舍五入),带千分符
  48. String formatAmount(double v, {int decimals = 2}) {
  49. if (v == 0) return decimals > 0 ? '0.${'0' * decimals}' : '0';
  50. final factor = _pow10(decimals);
  51. final truncated = (v * factor).truncateToDouble() / factor;
  52. final fixed = truncated.toStringAsFixed(decimals);
  53. final parts = fixed.split('.');
  54. final intPart = parts[0].replaceAllMapped(
  55. RegExp(r'(\d)(?=(\d{3})+$)'),
  56. (m) => '${m[1]},',
  57. );
  58. return parts.length > 1 ? '$intPart.${parts[1]}' : intPart;
  59. }
  60. /// 数量原样格式化:不截断精度,仅去除尾零
  61. /// 0.001 → "0.001" | 1.0 → "1" | 0.10 → "0.1"
  62. String formatQuantity(double v) {
  63. if (v == v.truncateToDouble()) return v.toInt().toString();
  64. // toStringAsFixed(8) 覆盖 8 位小数精度,再剥离尾零和多余小数点
  65. final s = v.toStringAsFixed(8);
  66. return s.replaceAll(RegExp(r'0+$'), '').replaceAll(RegExp(r'\.$'), '');
  67. }
  68. /// 成交量格式化(亿/万)
  69. String formatVolume(double volume) {
  70. if (volume >= 1e8) return '${(volume / 1e8).toStringAsFixed(2)}亿';
  71. if (volume >= 1e4) return '${(volume / 1e4).toStringAsFixed(2)}万';
  72. return volume.toStringAsFixed(2);
  73. }
  74. // ── 法币估值 ─────────────────────────────────────────────────
  75. /// 当前法币汇率(USDT → 目标法币),默认 1:1(USD)
  76. /// 后续切换法币时修改此值和符号即可全局生效
  77. double _fiatRate = 1.0;
  78. String _fiatSymbol = '\$';
  79. /// 设置法币汇率与符号(如 CNY: rate=7.25, symbol='¥')
  80. void setFiatCurrency({required double rate, required String symbol}) {
  81. _fiatRate = rate;
  82. _fiatSymbol = symbol;
  83. }
  84. /// 获取当前法币符号
  85. String get fiatSymbol => _fiatSymbol;
  86. /// 获取当前法币汇率
  87. double get fiatRate => _fiatRate;
  88. /// 将 USDT 价格转换为法币估值字符串
  89. /// 例: formatFiatPrice(0.31478, pricePrecision: 5) → "≈$0.31478"
  90. /// 例: formatFiatPrice(465.97, pricePrecision: 2) → "≈$466"
  91. String formatFiatPrice(double usdtPrice, {int? pricePrecision}) {
  92. final fiatValue = usdtPrice * _fiatRate;
  93. final decimals = pricePrecision ?? _autoDecimals(fiatValue);
  94. return '≈$_fiatSymbol${formatPrice(fiatValue, decimalPlaces: decimals)}';
  95. }
  96. /// 根据涨跌幅返回颜色
  97. Color changeColor(double change, BuildContext context) {
  98. if (change > 0) return AppColors.rise;
  99. if (change < 0) return AppColors.fall;
  100. return Theme.of(context).colorScheme.onSurface.withAlpha(128);
  101. }