import 'package:auto_route/auto_route.dart'; import 'package:flutter/material.dart'; import 'package:line_icons/line_icons.dart'; import '../../../common/theme/theme.dart'; import 'widgets/appbar.dart'; import 'widgets/cash_flow.dart'; import 'widgets/category.dart'; import 'widgets/profit_loss.dart'; import 'widgets/summary_card.dart'; @RoutePage() class FinancePage extends StatefulWidget { const FinancePage({super.key}); @override State createState() => _FinancePageState(); } class _FinancePageState extends State with TickerProviderStateMixin { late AnimationController _slideController; late AnimationController _fadeController; late AnimationController _scaleController; late AnimationController _rotationController; late AnimationController _floatingController; late Animation _slideAnimation; late Animation _fadeAnimation; late Animation _scaleAnimation; late Animation rotationAnimation; late Animation floatingAnimation; String selectedPeriod = 'Hari ini'; final List periods = [ 'Hari ini', 'Minggu ini', 'Bulan ini', 'Tahun ini', ]; @override void initState() { super.initState(); rotationAnimation = AnimationController( duration: const Duration(seconds: 20), vsync: this, )..repeat(); floatingAnimation = AnimationController( duration: const Duration(seconds: 3), vsync: this, )..repeat(reverse: true); _slideController = AnimationController( duration: const Duration(milliseconds: 800), vsync: this, ); _fadeController = AnimationController( duration: const Duration(milliseconds: 1000), vsync: this, ); _scaleController = AnimationController( duration: const Duration(milliseconds: 600), vsync: this, ); _slideAnimation = Tween(begin: const Offset(0, 0.3), end: Offset.zero).animate( CurvedAnimation(parent: _slideController, curve: Curves.easeOutCubic), ); _fadeAnimation = Tween( begin: 0.0, end: 1.0, ).animate(CurvedAnimation(parent: _fadeController, curve: Curves.easeIn)); _scaleAnimation = Tween(begin: 0.8, end: 1.0).animate( CurvedAnimation(parent: _scaleController, curve: Curves.elasticOut), ); // Start animations _fadeController.forward(); Future.delayed(const Duration(milliseconds: 200), () { _slideController.forward(); }); Future.delayed(const Duration(milliseconds: 400), () { _scaleController.forward(); }); } @override void dispose() { _slideController.dispose(); _fadeController.dispose(); _scaleController.dispose(); _rotationController.dispose(); _floatingController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( backgroundColor: AppColor.background, body: CustomScrollView( slivers: [ // SliverAppBar with animated background SliverAppBar( expandedHeight: 120, floating: false, pinned: true, backgroundColor: AppColor.primary, elevation: 0, flexibleSpace: FinanceAppbar( rotationAnimation: rotationAnimation, floatingAnimation: floatingAnimation, ), ), // Header dengan filter periode SliverToBoxAdapter( child: FadeTransition( opacity: _fadeAnimation, child: _buildPeriodSelector(), ), ), // Summary Cards SliverToBoxAdapter( child: SlideTransition( position: _slideAnimation, child: _buildSummaryCards(), ), ), // Cash Flow Analysis SliverToBoxAdapter( child: ScaleTransition( scale: _scaleAnimation, child: FinanceCashFlow(), ), ), // Profit Loss Detail SliverToBoxAdapter( child: FadeTransition( opacity: _fadeAnimation, child: FinanceProfitLoss(), ), ), // Transaction Categories SliverToBoxAdapter( child: SlideTransition( position: _slideAnimation, child: FinanceCategory(), ), ), // Monthly Comparison SliverToBoxAdapter( child: ScaleTransition( scale: _scaleAnimation, child: _buildMonthlyComparison(), ), ), // Bottom spacing const SliverToBoxAdapter(child: SizedBox(height: 100)), ], ), ); } Widget _buildPeriodSelector() { return Container( padding: const EdgeInsets.all(16), child: Row( children: [ Expanded( child: Container( padding: const EdgeInsets.symmetric(horizontal: 16), decoration: BoxDecoration( color: AppColor.white, borderRadius: BorderRadius.circular(12), border: Border.all(color: AppColor.border), ), child: DropdownButtonHideUnderline( child: DropdownButton( value: selectedPeriod, isExpanded: true, icon: const Icon( LineIcons.angleDown, color: AppColor.primary, ), style: AppStyle.md, items: periods.map((String period) { return DropdownMenuItem( value: period, child: Text(period), ); }).toList(), onChanged: (String? newValue) { setState(() { selectedPeriod = newValue!; }); }, ), ), ), ), const SizedBox(width: 12), Container( decoration: BoxDecoration( color: AppColor.primary, borderRadius: BorderRadius.circular(12), ), child: IconButton( onPressed: () {}, icon: const Icon(LineIcons.calendar, color: AppColor.white), ), ), ], ), ); } Widget _buildSummaryCards() { return Padding( padding: const EdgeInsets.symmetric(horizontal: 16), child: Column( children: [ Row( children: [ Expanded( child: FinanceSummaryCard( title: 'Total Pendapatan', amount: 'Rp 25.840.000', icon: LineIcons.arrowUp, color: AppColor.success, change: '+12.5%', isPositive: true, ), ), const SizedBox(width: 12), Expanded( child: FinanceSummaryCard( title: 'Total Pengeluaran', amount: 'Rp 18.320.000', icon: LineIcons.arrowDown, color: AppColor.error, change: '+8.2%', isPositive: false, ), ), ], ), const SizedBox(height: 12), Row( children: [ Expanded( child: FinanceSummaryCard( title: 'Keuntungan Bersih', amount: 'Rp 7.520.000', icon: LineIcons.lineChart, color: AppColor.info, change: '+15.3%', isPositive: true, ), ), const SizedBox(width: 12), Expanded( child: FinanceSummaryCard( title: 'Margin Profit', amount: '29.1%', icon: LineIcons.percent, color: AppColor.warning, change: '+2.1%', isPositive: true, ), ), ], ), ], ), ); } Widget _buildMonthlyComparison() { return Container( margin: const EdgeInsets.all(16), padding: const EdgeInsets.all(20), decoration: BoxDecoration( color: AppColor.white, borderRadius: BorderRadius.circular(16), boxShadow: [ BoxShadow( color: AppColor.textLight.withOpacity(0.1), spreadRadius: 1, blurRadius: 8, offset: const Offset(0, 2), ), ], ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Container( padding: const EdgeInsets.all(8), decoration: BoxDecoration( color: AppColor.warning.withOpacity(0.1), borderRadius: BorderRadius.circular(8), ), child: const Icon( LineIcons.calendarCheck, color: AppColor.warning, size: 20, ), ), const SizedBox(width: 12), Text( 'Perbandingan Bulanan', style: AppStyle.lg.copyWith(fontWeight: FontWeight.bold), ), ], ), const SizedBox(height: 20), Row( children: [ Expanded( child: _buildComparisonCard( 'Bulan Ini', 'Rp 7.52M', '+15.3%', true, AppColor.primary, ), ), const SizedBox(width: 12), Expanded( child: _buildComparisonCard( 'Bulan Lalu', 'Rp 6.53M', '-2.1%', false, AppColor.textSecondary, ), ), ], ), const SizedBox(height: 16), Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: AppColor.success.withOpacity(0.05), borderRadius: BorderRadius.circular(12), border: Border.all(color: AppColor.success.withOpacity(0.2)), ), child: Row( children: [ const Icon( LineIcons.thumbsUp, color: AppColor.success, size: 20, ), const SizedBox(width: 12), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Performa Bagus!', style: AppStyle.md.copyWith( fontWeight: FontWeight.bold, color: AppColor.success, ), ), Text( 'Keuntungan meningkat 15.3% dari bulan lalu', style: AppStyle.sm.copyWith( color: AppColor.textSecondary, ), ), ], ), ), ], ), ), ], ), ); } Widget _buildComparisonCard( String period, String amount, String change, bool isPositive, Color color, ) { return Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: color.withOpacity(0.05), borderRadius: BorderRadius.circular(12), border: Border.all(color: color.withOpacity(0.2)), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( period, style: AppStyle.sm.copyWith(color: AppColor.textSecondary), ), const SizedBox(height: 8), Text( amount, style: AppStyle.lg.copyWith( fontWeight: FontWeight.bold, color: color, ), ), const SizedBox(height: 4), Row( children: [ Icon( isPositive ? LineIcons.arrowUp : LineIcons.arrowDown, size: 14, color: isPositive ? AppColor.success : AppColor.error, ), const SizedBox(width: 4), Text( change, style: AppStyle.xs.copyWith( color: isPositive ? AppColor.success : AppColor.error, fontWeight: FontWeight.w600, ), ), ], ), ], ), ); } }