Files
submanager/lib/screens/analysis_screen.dart
JiWoong Sul ddf735149a feat: 알림 설정 개선 및 USD 환율 자동 적용
- 알림 권한 첫 부여 시 기본 설정 자동 적용 (2일전, 반복 알림 활성화)
- 반복 알림 설명 문구를 설정 상태에 따라 동적으로 변경
- USD 통화 구독에 대한 환율 자동 적용 기능 추가
- 설정 화면 텍스트 색상을 어두운 색상으로 변경하여 가독성 향상
- 광고 위젯 레이아웃 및 화면 간격 조정

🤖 Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>
2025-07-14 18:01:17 +09:00

158 lines
4.4 KiB
Dart

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../providers/subscription_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<AnalysisScreen> createState() => _AnalysisScreenState();
}
class _AnalysisScreenState extends State<AnalysisScreen>
with TickerProviderStateMixin {
late AnimationController _animationController;
late ScrollController _scrollController;
double _totalExpense = 0;
List<Map<String, dynamic>> _monthlyData = [];
int _touchedIndex = -1;
bool _isLoading = true;
@override
void initState() {
super.initState();
_animationController = AnimationController(
duration: const Duration(milliseconds: 1500),
vsync: this,
);
_scrollController = ScrollController();
_loadData();
}
@override
void dispose() {
_animationController.dispose();
_scrollController.dispose();
super.dispose();
}
Future<void> _loadData() async {
setState(() {
_isLoading = true;
});
final provider = Provider.of<SubscriptionProvider>(context, listen: false);
// 총 지출 계산
_totalExpense = await provider.calculateTotalExpense();
// 월별 데이터 계산
_monthlyData = await provider.getMonthlyExpenseData();
setState(() {
_isLoading = false;
});
// 데이터 로드 완료 후 애니메이션 시작
_animationController.forward();
}
Widget _buildAnimatedAd() {
return FadeTransition(
opacity: CurvedAnimation(
parent: _animationController,
curve: const Interval(0.0, 0.5, curve: Curves.easeOut),
),
child: SlideTransition(
position: Tween<Offset>(
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) {
return Consumer<SubscriptionProvider>(
builder: (context, provider, child) {
final subscriptions = provider.subscriptions;
if (_isLoading) {
return const Center(
child: CircularProgressIndicator(),
);
}
return CustomScrollView(
controller: _scrollController,
physics: const BouncingScrollPhysics(),
slivers: <Widget>[
SliverToBoxAdapter(
child: SizedBox(
height: kToolbarHeight + MediaQuery.of(context).padding.top,
),
),
// 네이티브 광고 위젯
SliverToBoxAdapter(
child: _buildAnimatedAd(),
),
const AnalysisScreenSpacer(),
// 1. 구독 비율 파이 차트
SubscriptionPieChartCard(
subscriptions: subscriptions,
animationController: _animationController,
touchedIndex: _touchedIndex,
onPieTouch: (index) => setState(() => _touchedIndex = index),
),
const AnalysisScreenSpacer(),
// 2. 총 지출 요약 카드
TotalExpenseSummaryCard(
subscriptions: subscriptions,
totalExpense: _totalExpense,
animationController: _animationController,
),
const AnalysisScreenSpacer(),
// 3. 월별 지출 차트
MonthlyExpenseChartCard(
monthlyData: _monthlyData,
animationController: _animationController,
),
const AnalysisScreenSpacer(),
// 4. 이벤트 분석
EventAnalysisCard(
animationController: _animationController,
),
// FloatingNavigationBar를 위한 충분한 하단 여백
SliverToBoxAdapter(
child: SizedBox(
height: 120 + MediaQuery.of(context).padding.bottom,
),
),
],
);
},
);
}
}