import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import '../../core/constants/app_constants.dart'; import '../../core/theme/app_theme.dart'; import '../../data/repositories/session_repository.dart'; import '../capture/capture_screen.dart'; import '../history/history_screen.dart'; import '../statistics/statistics_screen.dart'; import 'package:fl_chart/fl_chart.dart'; import '../../data/models/session.dart'; import '../settings/settings_screen.dart'; import '../session/session_setup_screen.dart'; import '../session/session_provider.dart'; import 'widgets/stats_card.dart'; class HomeScreen extends StatefulWidget { const HomeScreen({super.key}); @override State createState() => _HomeScreenState(); } class _HomeScreenState extends State { Map? _stats; List _recentSessions = []; bool _isLoading = true; SessionProvider? _sessionProvider; bool _wasSessionActive = false; @override void initState() { super.initState(); _loadStats(); } @override void didChangeDependencies() { super.didChangeDependencies(); final provider = context.read(); if (provider != _sessionProvider) { _sessionProvider?.removeListener(_onSessionChanged); _sessionProvider = provider; _sessionProvider!.addListener(_onSessionChanged); _wasSessionActive = provider.isSessionActive; } } void _onSessionChanged() { final isActive = _sessionProvider?.isSessionActive ?? false; // Quand une session vient de se terminer, on rafraîchit les stats // automatiquement pour prendre en compte la dernière session. if (_wasSessionActive && !isActive) { _loadStats(); } _wasSessionActive = isActive; } @override void dispose() { _sessionProvider?.removeListener(_onSessionChanged); super.dispose(); } Future _loadStats() async { final repository = context.read(); final stats = await repository.getStatistics(); final sessions = await repository.getAllSessions(); if (mounted) { setState(() { _stats = stats; _recentSessions = sessions; _isLoading = false; }); } } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( leading: const Center( child: Text( 'v1.0.2', style: TextStyle(fontSize: 12, color: Colors.white70), ), ), title: const Text('Bully'), actions: [ IconButton( icon: const Icon(Icons.settings), onPressed: () => _navigateToSettings(context), tooltip: 'Paramètres', ), ], ), body: RefreshIndicator( onRefresh: _loadStats, child: SingleChildScrollView( physics: const AlwaysScrollableScrollPhysics(), padding: const EdgeInsets.all(AppConstants.defaultPadding), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ _buildHeader(), const SizedBox(height: AppConstants.largePadding), _buildMainActionButton(context), const SizedBox(height: AppConstants.largePadding), if (_isLoading) const Center(child: CircularProgressIndicator()) else if (_stats != null) _buildStatsSection(), ], ), ), ), ); } Widget _buildHeader() { return Column( children: [ Container( padding: const EdgeInsets.all(24), decoration: BoxDecoration( color: AppTheme.primaryColor.withValues(alpha: 0.1), shape: BoxShape.circle, ), child: const Icon( Icons.track_changes, size: 64, color: AppTheme.primaryColor, ), ), const SizedBox(height: 16), Text( 'Analyse de Cibles', style: Theme.of( context, ).textTheme.headlineMedium?.copyWith(fontWeight: FontWeight.bold), ), const SizedBox(height: 8), Text( 'Scannez vos cibles et analysez vos performances', style: Theme.of( context, ).textTheme.bodyLarge?.copyWith(color: AppTheme.textSecondary), textAlign: TextAlign.center, ), ], ); } Widget _buildMainActionButton(BuildContext context) { final sessionProvider = context.watch(); final isSessionActive = sessionProvider.isSessionActive; // Si aucune session n'est active, on affiche juste le bouton de départ habituel if (!isSessionActive) { return ElevatedButton.icon( onPressed: () => _navigateToSessionSetup(context), style: ElevatedButton.styleFrom( backgroundColor: AppTheme.primaryColor, foregroundColor: Colors.white, padding: const EdgeInsets.symmetric(vertical: 20), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(AppConstants.borderRadius), ), ), icon: const Icon(Icons.add_circle_outline, size: 28), label: const Text( 'Nouvelle session', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), ), ); } // MODIFICATION : Alignement sur une seule ligne (80% / 20%) avec une croix pour l'annulation return Row( children: [ // 80% de largeur pour continuer la session Expanded( flex: 8, child: ElevatedButton.icon( onPressed: () => _navigateToSessionSetup(context), style: ElevatedButton.styleFrom( backgroundColor: AppTheme.secondaryColor, foregroundColor: Colors.white, padding: const EdgeInsets.symmetric(vertical: 20), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(AppConstants.borderRadius), ), ), icon: const Icon(Icons.play_circle_outline, size: 28), label: const Text( 'Continuer la session', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), maxLines: 1, overflow: TextOverflow.ellipsis, ), ), ), const SizedBox(width: 12), // 20% de largeur pour terminer/annuler la session (Bouton Croix) Expanded( flex: 2, child: OutlinedButton( onPressed: () { // Boite de dialogue de confirmation pour éviter les erreurs showDialog( context: context, builder: (BuildContext context) { return AlertDialog( title: const Text('Terminer la session ?'), content: const Text('Êtes-vous sûr de vouloir clôturer la session en cours ?'), actions: [ TextButton( onPressed: () => Navigator.pop(context), child: const Text('ANNULER'), ), TextButton( onPressed: () { Navigator.pop(context); // Clôture propre de la session dans le State global context.read().endSession(); ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('Session terminée avec succès !'), backgroundColor: AppTheme.successColor, ), ); }, child: const Text('TERMINER', style: TextStyle(color: Colors.redAccent)), ), ], ); }, ); }, style: OutlinedButton.styleFrom( foregroundColor: Colors.redAccent, side: const BorderSide(color: Colors.redAccent, width: 1.5), padding: const EdgeInsets.symmetric(vertical: 20), // Même hauteur que le bouton continuer shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(AppConstants.borderRadius), ), ), child: const Icon(Icons.close, size: 28), ), ), ], ); } Widget _buildStatsSection() { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Statistiques', style: Theme.of( context, ).textTheme.titleLarge?.copyWith(fontWeight: FontWeight.bold), ), const SizedBox(height: 12), Row( children: [ Expanded( child: InkWell( onTap: () => _navigateToStatistics(context), borderRadius: BorderRadius.circular(AppConstants.borderRadius), child: StatsCard( icon: Icons.assessment, title: 'Sessions', value: '${_stats!['totalSessions']}', color: AppTheme.primaryColor, ), ), ), const SizedBox(width: 12), Expanded( child: StatsCard( icon: Icons.gps_fixed, title: 'Tirs', value: '${_stats!['totalShots']}', color: AppTheme.secondaryColor, ), ), ], ), Padding( padding: const EdgeInsets.symmetric(vertical: 16.0), child: Container( height: 150, padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: Theme.of(context).cardColor, borderRadius: BorderRadius.circular(AppConstants.borderRadius), boxShadow: [ BoxShadow( color: Colors.black.withValues(alpha: 0.05), blurRadius: 10, offset: const Offset(0, 4), ), ], ), child: _recentSessions.length < 2 ? const Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon(Icons.show_chart, size: 40, color: Colors.grey), Text( 'Pas assez de données', style: TextStyle(color: Colors.grey, fontSize: 12), ), ], ), ) : _buildHomeTrendChart(), ), ), Row( children: [ Expanded( child: InkWell( onTap: () => _navigateToHistory(context), borderRadius: BorderRadius.circular(AppConstants.borderRadius), child: StatsCard( icon: Icons.trending_up, title: 'Historique', value: (_stats!['averageScore'] as double).toStringAsFixed(1), color: AppTheme.warningColor, ), ), ), const SizedBox(width: 12), Expanded( child: StatsCard( icon: Icons.emoji_events, title: 'Meilleur', value: '${_stats!['bestScore']}', color: AppTheme.successColor, ), ), ], ), ], ); } void _navigateToSessionSetup(BuildContext context) async { final sessionProvider = context.read(); if (sessionProvider.isSessionActive) { await Navigator.push( context, MaterialPageRoute(builder: (_) => const CaptureScreen()), ); } else { await Navigator.push( context, MaterialPageRoute(builder: (_) => const SessionSetupScreen()), ); } _loadStats(); } void _navigateToHistory(BuildContext context) async { await Navigator.push( context, MaterialPageRoute(builder: (_) => const HistoryScreen()), ); _loadStats(); } void _navigateToStatistics(BuildContext context) async { await Navigator.push( context, MaterialPageRoute(builder: (_) => const StatisticsScreen()), ); _loadStats(); } Widget _buildHomeTrendChart() { final sorted = List.from(_recentSessions) ..sort((a, b) => a.createdAt.compareTo(b.createdAt)); final display = sorted.length > 7 ? sorted.sublist(sorted.length - 7) : sorted; return LineChart( LineChartData( gridData: FlGridData( show: true, drawVerticalLine: false, getDrawingHorizontalLine: (value) => FlLine( color: Colors.grey.withValues(alpha: 0.1), strokeWidth: 1, ), ), titlesData: FlTitlesData( show: true, rightTitles: const AxisTitles(sideTitles: SideTitles(showTitles: false)), topTitles: const AxisTitles(sideTitles: SideTitles(showTitles: false)), bottomTitles: AxisTitles( sideTitles: SideTitles( showTitles: true, reservedSize: 22, interval: 1, getTitlesWidget: (value, meta) { final index = value.toInt(); if (index < 0 || index >= display.length) return const SizedBox(); final date = display[index].createdAt; return Padding( padding: const EdgeInsets.only(top: 4.0), child: Text( '${date.day}/${date.month}', style: TextStyle( fontSize: 9, color: Colors.grey.withValues(alpha: 0.8), ), ), ); }, ), ), leftTitles: AxisTitles( sideTitles: SideTitles( showTitles: true, reservedSize: 28, getTitlesWidget: (value, meta) { return Text( value.toInt().toString(), style: TextStyle( fontSize: 9, color: Colors.grey.withValues(alpha: 0.8), ), ); }, ), ), ), borderData: FlBorderData( show: true, border: Border( bottom: BorderSide(color: Colors.grey.withValues(alpha: 0.2)), left: BorderSide(color: Colors.grey.withValues(alpha: 0.2)), ), ), lineBarsData: [ LineChartBarData( spots: display.asMap().entries.map((e) { return FlSpot(e.key.toDouble(), e.value.averageScore); }).toList(), isCurved: true, color: AppTheme.primaryColor, barWidth: 3, isStrokeCapRound: true, dotData: FlDotData( show: true, getDotPainter: (spot, percent, barData, index) => FlDotCirclePainter( radius: 3, color: Colors.white, strokeWidth: 2, strokeColor: AppTheme.primaryColor, ), ), belowBarData: BarAreaData( show: true, color: AppTheme.primaryColor.withValues(alpha: 0.1), ), ), ], ), ); } void _navigateToSettings(BuildContext context) async { await Navigator.push( context, MaterialPageRoute(builder: (_) => const SettingsScreen()), ); _loadStats(); } }