import 'package:flutter/material.dart'; import 'package:fl_chart/fl_chart.dart'; import '../l10n/app_localizations.dart'; import '../utils/colors.dart'; import '../utils/score_manager.dart'; import '../utils/theme_notifier.dart'; import '../game/audio/audio_manager.dart'; class StatsScreen extends StatefulWidget { const StatsScreen({super.key}); @override State createState() => _StatsScreenState(); } class _StatsScreenState extends State with TickerProviderStateMixin { late TabController _tabController; bool _isLoading = true; @override void initState() { super.initState(); _tabController = TabController(length: 2, vsync: this); _loadData(); // Start menu music when entering stats screen WidgetsBinding.instance.addPostFrameCallback((_) { AudioManager().playMenuMusic(); }); } @override void dispose() { _tabController.dispose(); super.dispose(); } Future _loadData() async { // Ensure score manager is initialized await ScoreManager.initialize(); setState(() { _isLoading = false; }); } @override Widget build(BuildContext context) { return ThemeAwareBuilder( builder: (context, theme) { return Scaffold( backgroundColor: ZenColors.currentAppBackground, appBar: AppBar( backgroundColor: ZenColors.currentAppBackground, elevation: 0, leading: IconButton( onPressed: () => Navigator.of(context).pop(true), icon: Icon(Icons.arrow_back, color: ZenColors.currentPrimaryText), ), title: Text( AppLocalizations.of(context)!.statistics, style: TextStyle( color: ZenColors.currentPrimaryText, fontSize: 24, fontWeight: FontWeight.bold, ), ), centerTitle: true, bottom: TabBar( controller: _tabController, labelColor: ZenColors.currentButtonBackground, unselectedLabelColor: ZenColors.currentSecondaryText, indicatorColor: ZenColors.currentButtonBackground, tabs: [ Tab(text: AppLocalizations.of(context)!.overview), Tab(text: AppLocalizations.of(context)!.charts), ], ), ), body: _isLoading ? Center( child: CircularProgressIndicator( valueColor: AlwaysStoppedAnimation( ZenColors.currentButtonBackground, ), ), ) : TabBarView( controller: _tabController, children: [_buildOverviewTab(), _buildChartsTab()], ), ); }, ); } Widget _buildOverviewTab() { return SingleChildScrollView( padding: const EdgeInsets.all(20), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Quick Stats Cards _buildQuickStatsGrid(), const SizedBox(height: 30), // Achievements Section _buildAchievementsSection(), const SizedBox(height: 30), // Recent Activity _buildRecentActivitySection(), ], ), ); } Widget _buildQuickStatsGrid() { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( AppLocalizations.of(context)!.yourRelaxationJourney, style: TextStyle( color: ZenColors.currentPrimaryText, fontSize: 22, fontWeight: FontWeight.bold, ), ), const SizedBox(height: 15), LayoutBuilder( builder: (context, constraints) { final screenWidth = constraints.maxWidth; final crossAxisCount = screenWidth < 600 ? 2 : 3; final childAspectRatio = screenWidth < 400 ? 1.3 : 1.5; return GridView.count( crossAxisCount: crossAxisCount, shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), childAspectRatio: childAspectRatio, crossAxisSpacing: 15, mainAxisSpacing: 15, children: [ _buildStatCard( AppLocalizations.of(context)!.todaysPoints, ScoreManager.todayScore.toString(), Icons.today, ZenColors.currentButtonBackground, ), _buildStatCard( AppLocalizations.of(context)!.totalPoints, ScoreManager.totalScore.toString(), Icons.stars, ZenColors.red, ), _buildStatCard( AppLocalizations.of(context)!.bubblesPopped, ScoreManager.totalBubblesPopped.toString(), Icons.bubble_chart, ZenColors.navyBlue, ), _buildStatCard( AppLocalizations.of(context)!.dailyAverage, ScoreManager.averageDailyScore.toStringAsFixed(0), Icons.trending_up, Colors.green, ), _buildStatCard( AppLocalizations.of(context)!.currentStreak, AppLocalizations.of( context, )!.streakDays(ScoreManager.currentStreak), Icons.local_fire_department, Colors.orange, ), _buildStatCard( AppLocalizations.of(context)!.bestDay, ScoreManager.bestDayScore.toString(), Icons.military_tech, Colors.purple, ), ], ); }, ), ], ); } Widget _buildStatCard( String title, String value, IconData icon, Color color, ) { return Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: ZenColors.currentUiElements.withValues(alpha: 0.3), borderRadius: BorderRadius.circular(15), border: Border.all(color: color.withValues(alpha: 0.3), width: 1), ), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon(icon, color: color, size: 28), const SizedBox(height: 8), Text( value, style: TextStyle( color: ZenColors.currentPrimaryText, fontSize: 20, fontWeight: FontWeight.bold, ), ), const SizedBox(height: 4), Text( title, style: TextStyle( color: ZenColors.currentSecondaryText, fontSize: 12, ), textAlign: TextAlign.center, ), ], ), ); } Widget _buildAchievementsSection() { final achievements = _getAchievements(); return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( AppLocalizations.of(context)!.achievements, style: TextStyle( color: ZenColors.currentPrimaryText, fontSize: 22, fontWeight: FontWeight.bold, ), ), const SizedBox(height: 15), SizedBox( height: 120, child: ListView.builder( scrollDirection: Axis.horizontal, itemCount: achievements.length, itemBuilder: (context, index) { final achievement = achievements[index]; return Container( width: 100, margin: const EdgeInsets.only(right: 15), child: _buildAchievementCard( achievement['title']!, achievement['icon'] as IconData, achievement['unlocked'] as bool, ), ); }, ), ), ], ); } Widget _buildAchievementCard(String title, IconData icon, bool unlocked) { return Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: unlocked ? ZenColors.currentButtonBackground.withValues(alpha: 0.2) : ZenColors.currentUiElements.withValues(alpha: 0.1), borderRadius: BorderRadius.circular(12), border: Border.all( color: unlocked ? ZenColors.currentButtonBackground.withValues(alpha: 0.5) : ZenColors.currentUiElements.withValues(alpha: 0.3), width: 1, ), ), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( icon, color: unlocked ? ZenColors.currentButtonBackground : ZenColors.currentMutedText, size: 32, ), const SizedBox(height: 8), Text( title, style: TextStyle( color: unlocked ? ZenColors.currentPrimaryText : ZenColors.currentMutedText, fontSize: 11, fontWeight: unlocked ? FontWeight.w600 : FontWeight.normal, ), textAlign: TextAlign.center, maxLines: 2, overflow: TextOverflow.ellipsis, ), ], ), ); } List> _getAchievements() { final totalScore = ScoreManager.totalScore; final streak = ScoreManager.currentStreak; final bubbles = ScoreManager.totalBubblesPopped; final l10n = AppLocalizations.of(context)!; return [ { 'title': l10n.firstSteps, 'icon': Icons.baby_changing_station, 'unlocked': totalScore >= 10, }, { 'title': l10n.zenApprentice, 'icon': Icons.self_improvement, 'unlocked': totalScore >= 100, }, { 'title': l10n.bubbleMaster, 'icon': Icons.bubble_chart, 'unlocked': bubbles >= 100, }, { 'title': l10n.consistent, 'icon': Icons.calendar_today, 'unlocked': streak >= 3, }, { 'title': l10n.dedicated, 'icon': Icons.local_fire_department, 'unlocked': streak >= 7, }, { 'title': l10n.zenMaster, 'icon': Icons.psychology, 'unlocked': totalScore >= 1000, }, ]; } Widget _buildRecentActivitySection() { final lastDays = ScoreManager.getLastDaysScores(7); return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( AppLocalizations.of(context)!.last7Days, style: TextStyle( color: ZenColors.currentPrimaryText, fontSize: 22, fontWeight: FontWeight.bold, ), ), const SizedBox(height: 15), Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: ZenColors.currentUiElements.withValues(alpha: 0.2), borderRadius: BorderRadius.circular(15), ), child: Column( children: lastDays.map((entry) { final date = DateTime.parse(entry.key); final dayName = _getDayName(date.weekday); final isToday = _isToday(date); return Padding( padding: const EdgeInsets.symmetric(vertical: 4), child: LayoutBuilder( builder: (context, constraints) { final screenWidth = constraints.maxWidth; final dayWidth = screenWidth * 0.2; // 20% of available width final scoreWidth = screenWidth * 0.15; // 15% of available width return Row( children: [ SizedBox( width: dayWidth, child: Text( dayName, style: TextStyle( color: isToday ? ZenColors.currentButtonBackground : ZenColors.currentSecondaryText, fontSize: 14, fontWeight: isToday ? FontWeight.bold : FontWeight.normal, ), overflow: TextOverflow.ellipsis, ), ), Expanded( child: Container( height: 8, margin: const EdgeInsets.symmetric( horizontal: 10, ), child: LinearProgressIndicator( value: entry.value / (ScoreManager.bestDayScore.clamp( 1, double.infinity, )), backgroundColor: ZenColors.currentUiElements .withValues(alpha: 0.3), valueColor: AlwaysStoppedAnimation( isToday ? ZenColors.currentButtonBackground : ZenColors.currentUiElements, ), ), ), ), SizedBox( width: scoreWidth, child: Text( entry.value.toString(), style: TextStyle( color: isToday ? ZenColors.currentButtonBackground : ZenColors.currentPrimaryText, fontSize: 14, fontWeight: isToday ? FontWeight.bold : FontWeight.normal, ), textAlign: TextAlign.right, overflow: TextOverflow.ellipsis, ), ), ], ); }, ), ); }).toList(), ), ), ], ); } Widget _buildChartsTab() { return SingleChildScrollView( padding: const EdgeInsets.all(20), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ _buildDailyChart(), const SizedBox(height: 30), _buildWeeklyChart(), ], ), ); } Widget _buildDailyChart() { final lastDays = ScoreManager.getLastDaysScores(14); final maxScore = lastDays .fold(0, (max, entry) => entry.value > max ? entry.value : max) .clamp(1, double.infinity); return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( AppLocalizations.of(context)!.dailyProgress14Days, style: TextStyle( color: ZenColors.currentPrimaryText, fontSize: 22, fontWeight: FontWeight.bold, ), ), const SizedBox(height: 15), LayoutBuilder( builder: (context, constraints) { final chartHeight = constraints.maxWidth < 400 ? 250.0 : 300.0; final labelFontSize = constraints.maxWidth < 400 ? 10.0 : 12.0; return Container( height: chartHeight, padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: ZenColors.currentUiElements.withValues(alpha: 0.2), borderRadius: BorderRadius.circular(15), ), child: LineChart( LineChartData( backgroundColor: Colors.transparent, gridData: FlGridData( show: true, drawVerticalLine: false, getDrawingHorizontalLine: (value) { return FlLine( color: ZenColors.currentUiElements.withValues( alpha: 0.3, ), strokeWidth: 1, ); }, ), titlesData: FlTitlesData( bottomTitles: AxisTitles( sideTitles: SideTitles( showTitles: true, reservedSize: 30, interval: lastDays.length > 10 ? 2 : 1, getTitlesWidget: (value, meta) { if (value.toInt() >= 0 && value.toInt() < lastDays.length) { final date = DateTime.parse( lastDays[value.toInt()].key, ); return Transform.rotate( angle: constraints.maxWidth < 400 ? -0.5 : 0, child: Text( '${date.day}', style: TextStyle( color: ZenColors.currentSecondaryText, fontSize: labelFontSize, ), ), ); } return const Text(''); }, ), ), leftTitles: AxisTitles( sideTitles: SideTitles( showTitles: true, reservedSize: 40, getTitlesWidget: (value, meta) { return Text( value.toInt().toString(), style: TextStyle( color: ZenColors.currentSecondaryText, fontSize: labelFontSize, ), ); }, ), ), topTitles: const AxisTitles( sideTitles: SideTitles(showTitles: false), ), rightTitles: const AxisTitles( sideTitles: SideTitles(showTitles: false), ), ), borderData: FlBorderData(show: false), minX: 0, maxX: (lastDays.length - 1).toDouble(), minY: 0, maxY: maxScore.toDouble(), lineBarsData: [ LineChartBarData( spots: lastDays.asMap().entries.map((entry) { return FlSpot( entry.key.toDouble(), entry.value.value.toDouble(), ); }).toList(), isCurved: true, gradient: LinearGradient( colors: [ ZenColors.currentButtonBackground.withValues( alpha: 0.8, ), ZenColors.currentButtonBackground, ], ), barWidth: 3, dotData: FlDotData( show: true, getDotPainter: (spot, percent, barData, index) { return FlDotCirclePainter( radius: 4, color: ZenColors.currentButtonBackground, strokeWidth: 2, strokeColor: ZenColors.currentPrimaryText, ); }, ), belowBarData: BarAreaData( show: true, gradient: LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [ ZenColors.currentButtonBackground.withValues( alpha: 0.3, ), ZenColors.currentButtonBackground.withValues( alpha: 0.1, ), ], ), ), ), ], ), ), ); }, ), ], ); } Widget _buildWeeklyChart() { final weeklyScores = ScoreManager.getWeeklyScores(8); final maxScore = weeklyScores .fold(0, (max, entry) => entry.value > max ? entry.value : max) .clamp(1, double.infinity); return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( AppLocalizations.of(context)!.weeklySummary8Weeks, style: TextStyle( color: ZenColors.currentPrimaryText, fontSize: 22, fontWeight: FontWeight.bold, ), ), const SizedBox(height: 15), LayoutBuilder( builder: (context, constraints) { final chartHeight = constraints.maxWidth < 400 ? 250.0 : 300.0; final labelFontSize = constraints.maxWidth < 400 ? 10.0 : 12.0; return Container( height: chartHeight, padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: ZenColors.currentUiElements.withValues(alpha: 0.2), borderRadius: BorderRadius.circular(15), ), child: BarChart( BarChartData( backgroundColor: Colors.transparent, gridData: FlGridData( show: true, drawVerticalLine: false, getDrawingHorizontalLine: (value) { return FlLine( color: ZenColors.currentUiElements.withValues( alpha: 0.3, ), strokeWidth: 1, ); }, ), titlesData: FlTitlesData( bottomTitles: AxisTitles( sideTitles: SideTitles( showTitles: true, reservedSize: 30, getTitlesWidget: (value, meta) { if (value.toInt() >= 0 && value.toInt() < weeklyScores.length) { return Text( 'W${value.toInt() + 1}', style: TextStyle( color: ZenColors.currentSecondaryText, fontSize: labelFontSize, ), ); } return const Text(''); }, ), ), leftTitles: AxisTitles( sideTitles: SideTitles( showTitles: true, reservedSize: 40, getTitlesWidget: (value, meta) { return Text( value.toInt().toString(), style: TextStyle( color: ZenColors.currentSecondaryText, fontSize: labelFontSize, ), ); }, ), ), topTitles: const AxisTitles( sideTitles: SideTitles(showTitles: false), ), rightTitles: const AxisTitles( sideTitles: SideTitles(showTitles: false), ), ), borderData: FlBorderData(show: false), maxY: maxScore.toDouble(), barGroups: weeklyScores.asMap().entries.map((entry) { return BarChartGroupData( x: entry.key, barRods: [ BarChartRodData( toY: entry.value.value.toDouble(), gradient: LinearGradient( begin: Alignment.bottomCenter, end: Alignment.topCenter, colors: [ ZenColors.navyBlue.withValues(alpha: 0.8), ZenColors.navyBlue, ], ), width: 20, borderRadius: const BorderRadius.only( topLeft: Radius.circular(6), topRight: Radius.circular(6), ), ), ], ); }).toList(), ), ), ); }, ), ], ); } String _getDayName(int weekday) { final l10n = AppLocalizations.of(context)!; final days = [ l10n.monday, l10n.tuesday, l10n.wednesday, l10n.thursday, l10n.friday, l10n.saturday, l10n.sunday, ]; return days[weekday - 1]; } bool _isToday(DateTime date) { final now = DateTime.now(); return date.year == now.year && date.month == now.month && date.day == now.day; } }