- Implemented new navigation system with NavigationProvider and route management - Added adaptive theme system with ThemeProvider for better theme handling - Introduced glassmorphism design elements (app bars, scaffolds, cards) - Added advanced animations (spring animations, page transitions, staggered lists) - Implemented performance optimizations (memory manager, lazy loading) - Refactored Analysis screen into modular components - Added floating navigation bar with haptic feedback - Improved subscription cards with swipe actions - Enhanced skeleton loading with better animations - Added cached network image support - Improved overall app architecture and code organization 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
160 lines
4.5 KiB
Dart
160 lines
4.5 KiB
Dart
import 'package:intl/intl.dart';
|
|
import '../models/subscription_model.dart';
|
|
import 'exchange_rate_service.dart';
|
|
|
|
/// 통화 단위 변환 및 포맷팅을 위한 유틸리티 클래스
|
|
class CurrencyUtil {
|
|
static final ExchangeRateService _exchangeRateService = ExchangeRateService();
|
|
|
|
/// 구독 목록의 총 월 비용을 계산 (원화로 환산, 이벤트 가격 반영)
|
|
static Future<double> calculateTotalMonthlyExpense(
|
|
List<SubscriptionModel> subscriptions) async {
|
|
double total = 0.0;
|
|
|
|
for (var subscription in subscriptions) {
|
|
// 이벤트 가격이 있으면 currentPrice 사용
|
|
final price = subscription.currentPrice;
|
|
|
|
if (subscription.currency == 'USD') {
|
|
// USD인 경우 KRW로 변환
|
|
final krwAmount = await _exchangeRateService
|
|
.convertUsdToKrw(price);
|
|
if (krwAmount != null) {
|
|
total += krwAmount;
|
|
}
|
|
} else {
|
|
// KRW인 경우 그대로 합산
|
|
total += price;
|
|
}
|
|
}
|
|
|
|
return total;
|
|
}
|
|
|
|
/// 구독의 월 비용을 표시 형식에 맞게 변환 (원화 변환 포함, 이벤트 가격 반영)
|
|
static Future<String> formatSubscriptionAmount(
|
|
SubscriptionModel subscription) async {
|
|
// 이벤트 가격이 있으면 currentPrice 사용
|
|
final price = subscription.currentPrice;
|
|
|
|
if (subscription.currency == 'USD') {
|
|
// USD 표시 + 원화 환산 금액
|
|
final usdFormatted = NumberFormat.currency(
|
|
locale: 'en_US',
|
|
symbol: '\$',
|
|
decimalDigits: 2,
|
|
).format(price);
|
|
|
|
// 원화 환산 금액
|
|
final krwAmount = await _exchangeRateService
|
|
.getFormattedKrwAmount(price);
|
|
|
|
return '$usdFormatted $krwAmount';
|
|
} else {
|
|
// 원화 표시
|
|
return NumberFormat.currency(
|
|
locale: 'ko_KR',
|
|
symbol: '₩',
|
|
decimalDigits: 0,
|
|
).format(price);
|
|
}
|
|
}
|
|
|
|
/// 총액을 원화로 표시
|
|
static String formatTotalAmount(double amount) {
|
|
return NumberFormat.currency(
|
|
locale: 'ko_KR',
|
|
symbol: '₩',
|
|
decimalDigits: 0,
|
|
).format(amount);
|
|
}
|
|
|
|
/// 환율 정보 텍스트 가져오기
|
|
static Future<String> getExchangeRateInfo() {
|
|
return _exchangeRateService.getFormattedExchangeRateInfo();
|
|
}
|
|
|
|
/// 이벤트로 인한 총 절약액 계산 (원화로 환산)
|
|
static Future<double> calculateTotalEventSavings(
|
|
List<SubscriptionModel> subscriptions) async {
|
|
double total = 0.0;
|
|
|
|
for (var subscription in subscriptions) {
|
|
if (subscription.isCurrentlyInEvent) {
|
|
final savings = subscription.eventSavings;
|
|
|
|
if (subscription.currency == 'USD') {
|
|
// USD인 경우 KRW로 변환
|
|
final krwAmount = await _exchangeRateService
|
|
.convertUsdToKrw(savings);
|
|
if (krwAmount != null) {
|
|
total += krwAmount;
|
|
}
|
|
} else {
|
|
// KRW인 경우 그대로 합산
|
|
total += savings;
|
|
}
|
|
}
|
|
}
|
|
|
|
return total;
|
|
}
|
|
|
|
/// 이벤트 절약액을 표시 형식에 맞게 변환
|
|
static Future<String> formatEventSavings(
|
|
SubscriptionModel subscription) async {
|
|
if (!subscription.isCurrentlyInEvent) {
|
|
return '';
|
|
}
|
|
|
|
final savings = subscription.eventSavings;
|
|
|
|
if (subscription.currency == 'USD') {
|
|
// USD 표시 + 원화 환산 금액
|
|
final usdFormatted = NumberFormat.currency(
|
|
locale: 'en_US',
|
|
symbol: '\$',
|
|
decimalDigits: 2,
|
|
).format(savings);
|
|
|
|
// 원화 환산 금액
|
|
final krwAmount = await _exchangeRateService
|
|
.getFormattedKrwAmount(savings);
|
|
|
|
return '$usdFormatted $krwAmount';
|
|
} else {
|
|
// 원화 표시
|
|
return NumberFormat.currency(
|
|
locale: 'ko_KR',
|
|
symbol: '₩',
|
|
decimalDigits: 0,
|
|
).format(savings);
|
|
}
|
|
}
|
|
|
|
/// 금액과 통화를 받아 포맷팅하여 반환
|
|
static Future<String> formatAmount(double amount, String currency) async {
|
|
if (currency == 'USD') {
|
|
// USD 표시 + 원화 환산 금액
|
|
final usdFormatted = NumberFormat.currency(
|
|
locale: 'en_US',
|
|
symbol: '\$',
|
|
decimalDigits: 2,
|
|
).format(amount);
|
|
|
|
// 원화 환산 금액
|
|
final krwAmount = await _exchangeRateService
|
|
.getFormattedKrwAmount(amount);
|
|
|
|
return '$usdFormatted $krwAmount';
|
|
} else {
|
|
// 원화 표시
|
|
return NumberFormat.currency(
|
|
locale: 'ko_KR',
|
|
symbol: '₩',
|
|
decimalDigits: 0,
|
|
).format(amount);
|
|
}
|
|
}
|
|
}
|