import 'package:flutter/material.dart'; import 'package:fl_chart/fl_chart.dart'; import '../../models/subscription_model.dart'; import '../../services/currency_util.dart'; import '../glassmorphism_card.dart'; import '../themed_text.dart'; import 'analysis_badge.dart'; /// 구독 서비스 비율을 파이 차트로 보여주는 카드 위젯 class SubscriptionPieChartCard extends StatelessWidget { final List subscriptions; final AnimationController animationController; final int touchedIndex; final Function(int) onPieTouch; const SubscriptionPieChartCard({ super.key, required this.subscriptions, required this.animationController, required this.touchedIndex, required this.onPieTouch, }); // 파이 차트 섹션 데이터 List _getPieSections() { if (subscriptions.isEmpty) return []; final colors = [ const Color(0xFF3B82F6), const Color(0xFF10B981), const Color(0xFFF59E0B), const Color(0xFFEF4444), const Color(0xFF8B5CF6), const Color(0xFF0EA5E9), const Color(0xFFEC4899), ]; // 개별 구독의 비율 계산을 위한 값들 List sectionValues = []; // 각 구독의 원화 환산 금액 또는 원화 금액을 계산 for (var subscription in subscriptions) { double value = subscription.monthlyCost; if (subscription.currency == 'USD') { // USD의 경우 마지막으로 조회된 환율로 대략적인 계산 // (정확한 계산은 비동기로 이루어지므로 UI 표시용으로만 사용) const rate = 1350.0; // 기본 환율 (실제 값은 API로 별도로 가져옴) value = value * rate; } sectionValues.add(value); } // 총합 계산 double sectionsTotal = sectionValues.fold(0, (sum, value) => sum + value); // 섹션 데이터 생성 return List.generate(subscriptions.length, (i) { final subscription = subscriptions[i]; final percentage = (sectionValues[i] / sectionsTotal) * 100; final index = i % colors.length; final isTouched = touchedIndex == i; final fontSize = isTouched ? 16.0 : 12.0; final radius = isTouched ? 105.0 : 100.0; return PieChartSectionData( value: sectionValues[i], title: '${percentage.toStringAsFixed(1)}%', titleStyle: TextStyle( fontSize: fontSize, fontWeight: FontWeight.bold, color: Colors.white, shadows: const [ Shadow(color: Colors.black26, blurRadius: 2, offset: Offset(0, 1)) ], ), color: colors[index], radius: radius, titlePositionPercentageOffset: 0.6, badgeWidget: isTouched ? AnalysisBadge( size: 40, borderColor: colors[index], subscription: subscription, ) : null, badgePositionPercentageOffset: .98, ); }); } @override Widget build(BuildContext context) { return SliverToBoxAdapter( child: Padding( padding: const EdgeInsets.symmetric(horizontal: 16), child: FadeTransition( opacity: CurvedAnimation( parent: animationController, curve: const Interval(0.0, 0.7, curve: Curves.easeOut), ), child: SlideTransition( position: Tween( begin: const Offset(0, 0.2), end: Offset.zero, ).animate(CurvedAnimation( parent: animationController, curve: const Interval(0.0, 0.7, curve: Curves.easeOut), )), child: GlassmorphismCard( blur: 10, opacity: 0.1, borderRadius: 16, child: Padding( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ ThemedText.headline( text: '구독 서비스 비율', style: const TextStyle( fontSize: 18, ), ), FutureBuilder( future: CurrencyUtil.getExchangeRateInfo(), builder: (context, snapshot) { if (snapshot.hasData && snapshot.data!.isNotEmpty) { return Container( padding: const EdgeInsets.symmetric( horizontal: 8, vertical: 4, ), decoration: BoxDecoration( color: const Color(0xFFE5F2FF), borderRadius: BorderRadius.circular(4), border: Border.all( color: const Color(0xFFBFDBFE), width: 1, ), ), child: Text( snapshot.data!, style: const TextStyle( fontSize: 12, fontWeight: FontWeight.w500, color: Color(0xFF3B82F6), ), ), ); } return const SizedBox.shrink(); }, ), ], ), const SizedBox(height: 8), ThemedText.subtitle( text: '월 지출 기준', style: const TextStyle( fontSize: 14, ), ), const SizedBox(height: 16), Center( child: subscriptions.isEmpty ? const SizedBox( height: 250, child: Center( child: ThemedText( '구독중인 서비스가 없습니다', style: TextStyle( fontSize: 16, ), ), ), ) : SizedBox( height: 250, child: PieChart( PieChartData( borderData: FlBorderData(show: false), sectionsSpace: 2, centerSpaceRadius: 60, sections: _getPieSections(), pieTouchData: PieTouchData( touchCallback: (FlTouchEvent event, pieTouchResponse) { if (!event .isInterestedForInteractions || pieTouchResponse == null || pieTouchResponse .touchedSection == null) { onPieTouch(-1); return; } onPieTouch(pieTouchResponse .touchedSection! .touchedSectionIndex); }, ), ), ), ), ), const SizedBox(height: 16), // 서비스 목록 Column( children: subscriptions.isEmpty ? [] : List.generate( subscriptions.length, (index) { final subscription = subscriptions[index]; final color = [ const Color(0xFF3B82F6), const Color(0xFF10B981), const Color(0xFFF59E0B), const Color(0xFFEF4444), const Color(0xFF8B5CF6), const Color(0xFF0EA5E9), const Color(0xFFEC4899), ][index % 7]; return Padding( padding: const EdgeInsets.only( bottom: 4.0), child: Row( children: [ Container( width: 12, height: 12, decoration: BoxDecoration( color: color, shape: BoxShape.circle, ), ), const SizedBox(width: 8), Expanded( child: ThemedText( subscription.serviceName, style: const TextStyle( fontSize: 14, ), overflow: TextOverflow.ellipsis, ), ), FutureBuilder( future: CurrencyUtil .formatSubscriptionAmount( subscription), builder: (context, snapshot) { if (snapshot.hasData) { return ThemedText( snapshot.data!, style: const TextStyle( fontSize: 14, fontWeight: FontWeight.bold, ), ); } return const SizedBox( width: 20, height: 20, child: CircularProgressIndicator( strokeWidth: 2, ), ); }, ), ], ), ); }, ), ), ], ), ), ), ), ), ), ); } }