import 'package:flutter/material.dart'; import 'package:fl_chart/fl_chart.dart'; import 'dart:math' as math; import '../../services/currency_util.dart'; import '../glassmorphism_card.dart'; import '../themed_text.dart'; /// 월별 지출 현황을 차트로 보여주는 카드 위젯 class MonthlyExpenseChartCard extends StatelessWidget { final List> monthlyData; final AnimationController animationController; const MonthlyExpenseChartCard({ super.key, required this.monthlyData, required this.animationController, }); // 월간 지출 차트 데이터 List _getMonthlyBarGroups() { final List barGroups = []; final calculatedMax = monthlyData.fold( 0, (max, data) => math.max(max, data['totalExpense'] as double)); final maxAmount = calculatedMax > 0 ? calculatedMax : 100000.0; // 기본값 10만원 for (int i = 0; i < monthlyData.length; i++) { final data = monthlyData[i]; barGroups.add( BarChartGroupData( x: i, barRods: [ BarChartRodData( toY: data['totalExpense'], gradient: LinearGradient( colors: [ const Color(0xFF3B82F6).withValues(alpha: 0.7), const Color(0xFF60A5FA), ], begin: Alignment.bottomCenter, end: Alignment.topCenter, ), width: 18, borderRadius: BorderRadius.circular(4), backDrawRodData: BackgroundBarChartRodData( show: true, toY: maxAmount + (maxAmount * 0.1), color: Colors.grey.withValues(alpha: 0.1), ), ), ], ), ); } return barGroups; } @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.4, 0.9, curve: Curves.easeOut), ), child: SlideTransition( position: Tween( begin: const Offset(0, 0.2), end: Offset.zero, ).animate(CurvedAnimation( parent: animationController, curve: const Interval(0.4, 0.9, 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: [ ThemedText.headline( text: '월별 지출 현황', style: const TextStyle( fontSize: 18, ), ), const SizedBox(height: 8), ThemedText.subtitle( text: '최근 6개월간 추이', style: const TextStyle( fontSize: 14, ), ), const SizedBox(height: 20), // 바 차트 AspectRatio( aspectRatio: 1.6, child: BarChart( BarChartData( alignment: BarChartAlignment.spaceAround, maxY: math.max( monthlyData.fold( 0, (max, data) => math.max( max, data['totalExpense'] as double)) * 1.2, 100000.0 // 최소값 10만원 ), barGroups: _getMonthlyBarGroups(), gridData: FlGridData( show: true, drawVerticalLine: false, horizontalInterval: math.max( monthlyData.fold( 0, (max, data) => math.max(max, data['totalExpense'] as double)) / 4, 25000.0 // 최소 간격 2.5만원 ), getDrawingHorizontalLine: (value) { return FlLine( color: Colors.grey.withValues(alpha: 0.1), strokeWidth: 1, ); }, ), titlesData: FlTitlesData( show: true, topTitles: const AxisTitles( sideTitles: SideTitles(showTitles: false), ), bottomTitles: AxisTitles( sideTitles: SideTitles( showTitles: true, getTitlesWidget: (value, meta) { return Padding( padding: const EdgeInsets.only(top: 8), child: ThemedText.caption( text: monthlyData[value.toInt()] ['monthName'], style: const TextStyle( fontSize: 12, fontWeight: FontWeight.bold, ), ), ); }, ), ), leftTitles: const AxisTitles( sideTitles: SideTitles(showTitles: false), ), rightTitles: const AxisTitles( sideTitles: SideTitles(showTitles: false), ), ), borderData: FlBorderData(show: false), barTouchData: BarTouchData( enabled: true, touchTooltipData: BarTouchTooltipData( tooltipBgColor: Colors.blueGrey.shade800, tooltipRoundedRadius: 8, getTooltipItem: (group, groupIndex, rod, rodIndex) { return BarTooltipItem( '${monthlyData[group.x]['monthName']}\n', const TextStyle( color: Colors.white, fontWeight: FontWeight.bold, ), children: [ TextSpan( text: CurrencyUtil.formatTotalAmount( monthlyData[group.x]['totalExpense'] as double), style: const TextStyle( color: Colors.yellow, fontSize: 14, fontWeight: FontWeight.w500, ), ), ], ); }, ), ), ), ), ), const SizedBox(height: 16), Center( child: ThemedText.caption( text: '월 구독 지출', style: const TextStyle( fontSize: 12, fontWeight: FontWeight.bold, ), ), ), ], ), ), ), ), ), ), ); } }