import 'package:flutter/services.dart'; import 'package:intl/intl.dart'; /// 한국식 숫자 포맷팅 유틸리티 class KoreanNumberFormatter { static final _currencyFormat = NumberFormat.currency( locale: 'ko_KR', symbol: '₩', decimalDigits: 0, ); static final _decimalFormat = NumberFormat.decimalPattern('ko_KR'); /// 통화 포맷팅 (₩1,234,567) static String formatCurrency(num amount) { return _currencyFormat.format(amount); } /// 통화 포맷팅 - 심플 (1,234,567원) static String formatCurrencySimple(num amount) { return '${_decimalFormat.format(amount)}원'; } /// 수량 포맷팅 (1,234개) static String formatQuantity(int quantity, {String unit = '개'}) { return '${_decimalFormat.format(quantity)}$unit'; } /// 일반 숫자 포맷팅 (1,234,567) static String formatNumber(num number) { return _decimalFormat.format(number); } /// 퍼센트 포맷팅 (85.2%) static String formatPercent(double value, {int decimalDigits = 1}) { final formatter = NumberFormat.percentPattern('ko_KR') ..minimumFractionDigits = decimalDigits ..maximumFractionDigits = decimalDigits; return formatter.format(value); } /// 파일 크기 포맷팅 (1.5MB, 256KB 등) static String formatFileSize(int bytes) { if (bytes < 1024) { return '${bytes}B'; } else if (bytes < 1024 * 1024) { return '${(bytes / 1024).toStringAsFixed(1)}KB'; } else if (bytes < 1024 * 1024 * 1024) { return '${(bytes / (1024 * 1024)).toStringAsFixed(1)}MB'; } else { return '${(bytes / (1024 * 1024 * 1024)).toStringAsFixed(2)}GB'; } } /// 한국식 단위 포맷팅 (만, 억, 조) static String formatKoreanUnit(num number) { final abs = number.abs(); final isNegative = number < 0; String result; if (abs >= 1000000000000) { // 조 단위 result = '${(abs / 1000000000000).toStringAsFixed(1)}조'; } else if (abs >= 100000000) { // 억 단위 result = '${(abs / 100000000).toStringAsFixed(1)}억'; } else if (abs >= 10000) { // 만 단위 result = '${(abs / 10000).toStringAsFixed(1)}만'; } else { result = _decimalFormat.format(abs); } return isNegative ? '-$result' : result; } /// 기간 포맷팅 (3개월, 1년 2개월 등) static String formatPeriod(int months) { if (months == 0) return '0개월'; final years = months ~/ 12; final remainingMonths = months % 12; if (years == 0) { return '$remainingMonths개월'; } else if (remainingMonths == 0) { return '$years년'; } else { return '$years년 $remainingMonths개월'; } } /// 남은 일수 포맷팅 static String formatRemainingDays(int days) { if (days < 0) { return '${-days}일 지남'; } else if (days == 0) { return '오늘'; } else if (days == 1) { return '내일'; } else if (days <= 7) { return '$days일 남음'; } else if (days <= 30) { final weeks = days ~/ 7; return '$weeks주 남음'; } else if (days <= 365) { final months = days ~/ 30; return '$months개월 남음'; } else { final years = days ~/ 365; return '$years년 남음'; } } } /// 숫자 입력 필드용 TextInputFormatter class ThousandsSeparatorInputFormatter extends TextInputFormatter { final String separator; ThousandsSeparatorInputFormatter({this.separator = ','}); @override TextEditingValue formatEditUpdate( TextEditingValue oldValue, TextEditingValue newValue, ) { // 숫자만 추출 final digitsOnly = newValue.text.replaceAll(RegExp(r'[^\d]'), ''); if (digitsOnly.isEmpty) { return const TextEditingValue(); } // 천 단위 구분자 추가 final formatted = _addThousandsSeparator(digitsOnly); // 커서 위치 계산 int cursorPosition = formatted.length; // 입력 중 커서 위치 조정 if (newValue.selection.baseOffset < newValue.text.length) { final beforeCursor = newValue.text.substring(0, newValue.selection.baseOffset); final digitsBeforeCursor = beforeCursor.replaceAll(RegExp(r'[^\d]'), '').length; cursorPosition = 0; int digitCount = 0; for (int i = 0; i < formatted.length; i++) { if (formatted[i] != separator) { digitCount++; } cursorPosition++; if (digitCount == digitsBeforeCursor) { break; } } } return TextEditingValue( text: formatted, selection: TextSelection.collapsed(offset: cursorPosition), ); } String _addThousandsSeparator(String digitsOnly) { final buffer = StringBuffer(); final length = digitsOnly.length; for (int i = 0; i < length; i++) { buffer.write(digitsOnly[i]); final remaining = length - i - 1; if (remaining > 0 && remaining % 3 == 0) { buffer.write(separator); } } return buffer.toString(); } } /// 통화 입력 필드용 TextInputFormatter class CurrencyInputFormatter extends TextInputFormatter { @override TextEditingValue formatEditUpdate( TextEditingValue oldValue, TextEditingValue newValue, ) { // 숫자만 추출 final digitsOnly = newValue.text.replaceAll(RegExp(r'[^\d]'), ''); if (digitsOnly.isEmpty) { return const TextEditingValue(); } // 천 단위 구분자 추가 final number = int.tryParse(digitsOnly) ?? 0; final formatted = '₩${KoreanNumberFormatter.formatNumber(number)}'; return TextEditingValue( text: formatted, selection: TextSelection.collapsed(offset: formatted.length), ); } }