import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import '../providers/subscription_provider.dart'; import '../providers/locale_provider.dart'; import '../widgets/native_ad_widget.dart'; import '../widgets/analysis/analysis_screen_spacer.dart'; import '../widgets/analysis/subscription_pie_chart_card.dart'; import '../widgets/analysis/total_expense_summary_card.dart'; import '../widgets/analysis/monthly_expense_chart_card.dart'; import '../widgets/analysis/event_analysis_card.dart'; class AnalysisScreen extends StatefulWidget { const AnalysisScreen({super.key}); @override State createState() => _AnalysisScreenState(); } class _AnalysisScreenState extends State with TickerProviderStateMixin { late AnimationController _animationController; late ScrollController _scrollController; double _totalExpense = 0; List> _monthlyData = []; bool _isLoading = true; String _lastDataHash = ''; @override void initState() { super.initState(); _animationController = AnimationController( duration: const Duration(milliseconds: 1500), vsync: this, ); _scrollController = ScrollController(); _loadData(); } @override void didChangeDependencies() { super.didChangeDependencies(); // Provider 변경 감지 final provider = Provider.of(context); final currentHash = _calculateDataHash(provider); debugPrint('[AnalysisScreen] didChangeDependencies: ' '현재 해시=$currentHash, 이전 해시=$_lastDataHash, 로딩중=$_isLoading'); // 데이터가 변경되었고 현재 로딩 중이 아닌 경우에만 리로드 if (currentHash != _lastDataHash && !_isLoading && _lastDataHash.isNotEmpty) { debugPrint('[AnalysisScreen] 데이터 변경 감지됨, 리로드 시작'); _loadData(); } } @override void dispose() { _animationController.dispose(); _scrollController.dispose(); super.dispose(); } /// 구독 데이터의 해시값을 계산하여 변경 감지 String _calculateDataHash(SubscriptionProvider provider) { final subscriptions = provider.subscriptions; final buffer = StringBuffer(); buffer.write(subscriptions.length); buffer.write('_'); buffer.write(provider.totalMonthlyExpense.toStringAsFixed(2)); for (final sub in subscriptions) { buffer.write( '_${sub.id}_${sub.currentPrice.toStringAsFixed(2)}_${sub.currency}'); } return buffer.toString(); } Future _loadData() async { debugPrint('[AnalysisScreen] _loadData 호출됨'); setState(() { _isLoading = true; }); final provider = Provider.of(context, listen: false); final localeProvider = Provider.of(context, listen: false); final locale = localeProvider.locale.languageCode; // 총 지출 계산 (로케일별 기본 통화로 환산) _totalExpense = await provider.calculateTotalExpense(locale: locale); debugPrint('[AnalysisScreen] 총 지출 계산 완료: $_totalExpense'); // 월별 데이터 계산 (로케일별 기본 통화로 환산) _monthlyData = await provider.getMonthlyExpenseData(locale: locale); debugPrint('[AnalysisScreen] 월별 데이터 계산 완료: ${_monthlyData.length}개월'); // 현재 데이터 해시값 저장 _lastDataHash = _calculateDataHash(provider); debugPrint('[AnalysisScreen] 데이터 해시값 저장: $_lastDataHash'); setState(() { _isLoading = false; }); // 데이터 로드 완료 후 애니메이션 시작 _animationController.reset(); _animationController.forward(); } Widget _buildAnimatedAd() { return FadeTransition( opacity: CurvedAnimation( parent: _animationController, curve: const Interval(0.0, 0.5, 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.5, curve: Curves.easeOut), )), child: const NativeAdWidget(key: ValueKey('analysis_ad')), ), ); } @override Widget build(BuildContext context) { // Provider를 직접 사용하여 변경 감지 final provider = Provider.of(context); final subscriptions = provider.subscriptions; if (_isLoading) { return const Center( child: CircularProgressIndicator(), ); } return CustomScrollView( controller: _scrollController, physics: const BouncingScrollPhysics(), slivers: [ SliverToBoxAdapter( child: SizedBox( height: kToolbarHeight + MediaQuery.of(context).padding.top, ), ), // 네이티브 광고 위젯 SliverToBoxAdapter( child: _buildAnimatedAd(), ), const AnalysisScreenSpacer(), // 1. 구독 비율 파이 차트 SubscriptionPieChartCard( subscriptions: subscriptions, animationController: _animationController, ), const AnalysisScreenSpacer(), // 2. 총 지출 요약 카드 TotalExpenseSummaryCard( key: ValueKey('total_expense_$_lastDataHash'), subscriptions: subscriptions, totalExpense: _totalExpense, animationController: _animationController, ), const AnalysisScreenSpacer(), // 3. 월별 지출 차트 MonthlyExpenseChartCard( key: ValueKey('monthly_expense_$_lastDataHash'), monthlyData: _monthlyData, animationController: _animationController, ), const AnalysisScreenSpacer(), // 4. 이벤트 분석 EventAnalysisCard( animationController: _animationController, ), // FloatingNavigationBar를 위한 충분한 하단 여백 SliverToBoxAdapter( child: SizedBox( height: 120 + MediaQuery.of(context).padding.bottom, ), ), ], ); } }