import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:go_router/go_router.dart'; import '../providers/app_state.dart'; import '../services/statistics_service.dart'; class HomeScreen extends StatefulWidget { const HomeScreen({super.key}); @override State createState() => _HomeScreenState(); } class _HomeScreenState extends State with TickerProviderStateMixin { CoffeeStatistics? statistics; bool loading = true; late AnimationController _hoverController; @override void initState() { super.initState(); _hoverController = AnimationController( duration: const Duration(milliseconds: 300), vsync: this, ); _loadStatistics(); } @override void dispose() { _hoverController.dispose(); super.dispose(); } Future _loadStatistics() async { try { setState(() => loading = true); final appState = Provider.of(context, listen: false); final stats = await StatisticsService.getStatistics( appState.beans, appState.machines, appState.recipes, appState.journalEntries, appState.drinks, ); setState(() => statistics = stats); } catch (error) { debugPrint('Error loading statistics: $error'); } finally { setState(() => loading = false); } } @override Widget build(BuildContext context) { return Consumer( builder: (context, appState, child) { return LayoutBuilder( builder: (context, constraints) { final isLargeScreen = constraints.maxWidth > 1200; return Container( width: double.infinity, height: double.infinity, decoration: const BoxDecoration( gradient: LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [Color(0xFF1A1A1A), Color(0xFF2D2D2D)], ), ), child: Stack( children: [ // Background gradient effects exactly like React Positioned.fill( child: Container( decoration: const BoxDecoration( gradient: RadialGradient( center: Alignment(0.2, -0.5), radius: 1.0, colors: [ Color.fromRGBO(212, 165, 116, 0.1), Colors.transparent, ], ), ), ), ), Positioned.fill( child: Container( decoration: const BoxDecoration( gradient: RadialGradient( center: Alignment(-0.8, 0.2), radius: 1.0, colors: [ Color.fromRGBO(139, 69, 19, 0.1), Colors.transparent, ], ), ), ), ), // Main content with responsive layout SingleChildScrollView( padding: EdgeInsets.only( bottom: 100, left: constraints.maxWidth > 1200 ? (constraints.maxWidth - 1200) / 2 + 16 : 16, right: constraints.maxWidth > 1200 ? (constraints.maxWidth - 1200) / 2 + 16 : 16, ), child: ConstrainedBox( constraints: BoxConstraints( maxWidth: isLargeScreen ? 1200 : double.infinity, ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ _buildWelcomeSection(constraints), const SizedBox(height: 16), _buildNavigationCards(constraints), const SizedBox(height: 32), _buildStatsSection(constraints), if (statistics?.recentActivity.isNotEmpty == true) ...[ const SizedBox(height: 32), _buildRecentActivitySection(constraints), ], const SizedBox(height: 32), _buildCoffeeTipSection(constraints), ], ), ), ), ], ), ); }, ); }, ); } Widget _buildWelcomeSection(BoxConstraints constraints) { final isLargeScreen = constraints.maxWidth > 1200; final isMediumScreen = constraints.maxWidth > 600; return Container( margin: const EdgeInsets.symmetric(horizontal: 8, vertical: 0), padding: EdgeInsets.all(isMediumScreen ? 16 : 12), decoration: BoxDecoration( gradient: const LinearGradient( begin: Alignment.topLeft, end: Alignment.bottomRight, colors: [ Color.fromRGBO(212, 165, 116, 0.1), Color.fromRGBO(139, 69, 19, 0.1), ], ), borderRadius: BorderRadius.circular(8), border: Border.all( color: const Color.fromRGBO(212, 165, 116, 0.2), width: 1, ), ), child: Row( children: [ Icon( Icons.local_cafe, size: isLargeScreen ? 40 : (isMediumScreen ? 36 : 32), color: const Color(0xFFD4A574), ), SizedBox(width: isMediumScreen ? 16 : 12), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Welcome to Coffee at Home', style: TextStyle( fontSize: isLargeScreen ? 28 : (isMediumScreen ? 24 : 20), fontWeight: FontWeight.w600, color: const Color(0xFFF5F5DC), ), ), const SizedBox(height: 4), Text( 'Track your coffee journey with beans, equipment, recipes, and daily notes.', style: TextStyle( fontSize: isLargeScreen ? 16 : (isMediumScreen ? 14 : 12), color: const Color(0xFFD2B48C), ), ), ], ), ), ], ), ); } Widget _buildNavigationCards(BoxConstraints constraints) { final isLargeScreen = constraints.maxWidth > 1200; // Responsive grid layout like React flexbox if (isLargeScreen) { return Row( children: [ Expanded( child: _buildNavigationCard( 'Coffee Beans', 'Track your favorite beans and their origins', Icons.coffee, () => context.go('/beans'), constraints, ), ), const SizedBox(width: 8), Expanded( child: _buildNavigationCard( 'Equipment', 'Manage your coffee machines and tools', Icons.kitchen, () => context.go('/machines'), constraints, ), ), const SizedBox(width: 8), Expanded( child: _buildNavigationCard( 'Recipes', 'Perfect your brewing techniques', Icons.star, () => context.go('/recipes'), constraints, ), ), const SizedBox(width: 8), Expanded( child: _buildNavigationCard( 'Journal', 'Record your daily coffee experiences', Icons.book, () => context.go('/journal'), constraints, ), ), ], ); } else { return Wrap( spacing: 8, runSpacing: 8, children: [ _buildNavigationCard( 'Coffee Beans', 'Track your favorite beans and their origins', Icons.coffee, () => context.go('/beans'), constraints, ), _buildNavigationCard( 'Equipment', 'Manage your coffee machines and tools', Icons.kitchen, () => context.go('/machines'), constraints, ), _buildNavigationCard( 'Recipes', 'Perfect your brewing techniques', Icons.star, () => context.go('/recipes'), constraints, ), _buildNavigationCard( 'Journal', 'Record your daily coffee experiences', Icons.book, () => context.go('/journal'), constraints, ), ], ); } } Widget _buildNavigationCard( String title, String subtitle, IconData icon, VoidCallback onTap, BoxConstraints constraints, ) { final isLargeScreen = constraints.maxWidth > 1200; final isMediumScreen = constraints.maxWidth > 600; final cardWidth = isLargeScreen ? null : (constraints.maxWidth - 32) / 2 - 4; return MouseRegion( onEnter: (_) => _hoverController.forward(), onExit: (_) => _hoverController.reverse(), child: AnimatedBuilder( animation: _hoverController, builder: (context, child) { return AnimatedContainer( duration: const Duration(milliseconds: 300), curve: Curves.easeInOut, width: cardWidth, transform: Matrix4.translationValues( 0, -2 * _hoverController.value, 0, ), child: Card( elevation: 0, margin: EdgeInsets.zero, child: Material( color: Colors.transparent, child: InkWell( onTap: onTap, borderRadius: BorderRadius.circular(12), hoverColor: const Color.fromRGBO(212, 165, 116, 0.1), child: Container( padding: EdgeInsets.all(isMediumScreen ? 16 : 12), decoration: BoxDecoration( gradient: const LinearGradient( begin: Alignment.topLeft, end: Alignment.bottomRight, colors: [Color(0xFF2D2D2D), Color(0xFF3A3A3A)], ), borderRadius: BorderRadius.circular(12), border: const Border( top: BorderSide(color: Color(0xFFD4A574), width: 4), ), boxShadow: [ BoxShadow( color: Colors.black.withValues(alpha: 0.4), blurRadius: 12 + (8 * _hoverController.value), offset: Offset(0, 4 + (2 * _hoverController.value)), ), if (_hoverController.value > 0) BoxShadow( color: const Color( 0xFFD4A574, ).withValues(alpha: 0.2 * _hoverController.value), blurRadius: 20, offset: const Offset(0, 6), ), ], ), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( icon, size: isLargeScreen ? 48 : (isMediumScreen ? 44 : 40), color: const Color(0xFFD4A574), ), SizedBox(height: isMediumScreen ? 16 : 12), Text( title, style: TextStyle( fontSize: isLargeScreen ? 18 : (isMediumScreen ? 16 : 14), fontWeight: FontWeight.w600, color: const Color(0xFFF5F5DC), ), textAlign: TextAlign.center, ), SizedBox(height: isMediumScreen ? 8 : 6), Text( subtitle, style: TextStyle( fontSize: isLargeScreen ? 14 : (isMediumScreen ? 12 : 10), color: const Color(0xFFD2B48C), ), textAlign: TextAlign.center, ), ], ), ), ), ), ), ); }, ), ); } Widget _buildStatsSection(BoxConstraints constraints) { final isLargeScreen = constraints.maxWidth > 1200; final isMediumScreen = constraints.maxWidth > 600; return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Container( margin: const EdgeInsets.symmetric(horizontal: 8), padding: EdgeInsets.all(isMediumScreen ? 12 : 10), decoration: BoxDecoration( gradient: const LinearGradient( begin: Alignment.topLeft, end: Alignment.bottomRight, colors: [ Color.fromRGBO(212, 165, 116, 0.1), Color.fromRGBO(139, 69, 19, 0.1), ], ), borderRadius: BorderRadius.circular(8), border: Border.all( color: const Color.fromRGBO(212, 165, 116, 0.2), width: 1, ), ), child: Row( children: [ const Icon(Icons.star, color: Color(0xFFD4A574)), const SizedBox(width: 8), Text( 'Quick Stats', style: TextStyle( fontSize: isLargeScreen ? 24 : (isMediumScreen ? 20 : 18), fontWeight: FontWeight.w500, color: const Color(0xFFF5F5DC), ), ), ], ), ), const SizedBox(height: 16), _buildStatsGrid(constraints), ], ); } Widget _buildStatsGrid(BoxConstraints constraints) { final isLargeScreen = constraints.maxWidth > 1200; if (isLargeScreen) { return Column( children: [ Row( children: [ Expanded( child: _buildStatsCard( loading ? '...' : (statistics?.totalBeans ?? 0).toString(), 'Coffee Beans Tracked', statistics?.preferredBeans != null ? '${statistics!.preferredBeans} preferred' : null, constraints, ), ), const SizedBox(width: 8), Expanded( child: _buildStatsCard( loading ? '...' : (statistics?.totalMachines ?? 0).toString(), 'Machines Registered', null, constraints, ), ), const SizedBox(width: 8), Expanded( child: _buildStatsCard( loading ? '...' : (statistics?.totalJournalEntries ?? 0).toString(), 'Journal Entries', statistics?.monthlyStats['growth'] != null ? '${statistics!.monthlyStats['growth'] >= 0 ? '+' : ''}${statistics!.monthlyStats['growth']}% this month' : null, constraints, ), ), ], ), const SizedBox(height: 8), Row( children: [ Expanded( child: _buildStatsCard( loading ? '...' : (statistics?.totalRecipes ?? 0).toString(), 'Recipes Created', null, constraints, ), ), const SizedBox(width: 8), Expanded( child: _buildStatsCard( loading ? '...' : (statistics?.averageRating.toStringAsFixed(1) ?? '0.0'), 'Average Rating', statistics?.totalDrinks != null ? '${statistics!.totalDrinks} drinks rated' : null, constraints, ), ), const SizedBox(width: 8), Expanded( child: _buildStatsCard( loading ? '...' : (statistics?.mostUsedRoastLevel ?? 'N/A'), 'Favorite Roast', null, constraints, ), ), ], ), ], ); } else { return Wrap( spacing: 8, runSpacing: 8, children: [ _buildStatsCard( loading ? '...' : (statistics?.totalBeans ?? 0).toString(), 'Coffee Beans Tracked', statistics?.preferredBeans != null ? '${statistics!.preferredBeans} preferred' : null, constraints, ), _buildStatsCard( loading ? '...' : (statistics?.totalMachines ?? 0).toString(), 'Machines Registered', null, constraints, ), _buildStatsCard( loading ? '...' : (statistics?.totalJournalEntries ?? 0).toString(), 'Journal Entries', statistics?.monthlyStats['growth'] != null ? '${statistics!.monthlyStats['growth'] >= 0 ? '+' : ''}${statistics!.monthlyStats['growth']}% this month' : null, constraints, ), _buildStatsCard( loading ? '...' : (statistics?.totalRecipes ?? 0).toString(), 'Recipes Created', null, constraints, ), _buildStatsCard( loading ? '...' : (statistics?.averageRating.toStringAsFixed(1) ?? '0.0'), 'Average Rating', statistics?.totalDrinks != null ? '${statistics!.totalDrinks} drinks rated' : null, constraints, ), _buildStatsCard( loading ? '...' : (statistics?.mostUsedRoastLevel ?? 'N/A'), 'Favorite Roast', null, constraints, ), ], ); } } Widget _buildStatsCard( String value, String label, String? subtitle, BoxConstraints constraints, ) { final isLargeScreen = constraints.maxWidth > 1200; final isMediumScreen = constraints.maxWidth > 600; final cardWidth = isLargeScreen ? null : (constraints.maxWidth - 32) / 2 - 4; return MouseRegion( child: AnimatedContainer( duration: const Duration(milliseconds: 300), width: cardWidth, child: Card( elevation: 0, margin: EdgeInsets.zero, child: Container( padding: EdgeInsets.all(isMediumScreen ? 16 : 12), decoration: const BoxDecoration( gradient: LinearGradient( begin: Alignment.topLeft, end: Alignment.bottomRight, colors: [Color(0xFF6F4E37), Color(0xFF8B4513)], ), borderRadius: BorderRadius.all(Radius.circular(12)), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( value, style: TextStyle( fontSize: isLargeScreen ? 28 : (isMediumScreen ? 24 : 20), fontWeight: FontWeight.bold, color: const Color(0xFFD4A574), ), ), const SizedBox(height: 4), Text( label, style: TextStyle( fontSize: isLargeScreen ? 14 : (isMediumScreen ? 12 : 10), color: const Color(0xFFF5F5DC), ), ), if (subtitle != null) ...[ const SizedBox(height: 8), Row( children: [ if (subtitle.contains('this month')) Icon( Icons.trending_up, size: isMediumScreen ? 16 : 14, color: subtitle.contains('+') ? Colors.green : Colors.red, ), if (subtitle.contains('drinks rated')) Icon( Icons.star, size: isMediumScreen ? 16 : 14, color: const Color(0xFFD4A574), ), const SizedBox(width: 4), Expanded( child: Text( subtitle, style: TextStyle( fontSize: isLargeScreen ? 12 : (isMediumScreen ? 10 : 9), color: subtitle.contains('this month') ? (subtitle.contains('+') ? Colors.green : Colors.red) : const Color(0xFFD2B48C), ), ), ), ], ), ], ], ), ), ), ), ); } Widget _buildRecentActivitySection(BoxConstraints constraints) { final isLargeScreen = constraints.maxWidth > 1200; final isMediumScreen = constraints.maxWidth > 600; return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Container( margin: const EdgeInsets.symmetric(horizontal: 8), padding: EdgeInsets.all(isMediumScreen ? 12 : 10), decoration: BoxDecoration( gradient: const LinearGradient( begin: Alignment.topLeft, end: Alignment.bottomRight, colors: [ Color.fromRGBO(212, 165, 116, 0.1), Color.fromRGBO(139, 69, 19, 0.1), ], ), borderRadius: BorderRadius.circular(8), border: Border.all( color: const Color.fromRGBO(212, 165, 116, 0.2), width: 1, ), ), child: Row( children: [ const Icon(Icons.book, color: Color(0xFFD4A574)), const SizedBox(width: 8), Text( 'Recent Activity', style: TextStyle( fontSize: isLargeScreen ? 24 : (isMediumScreen ? 20 : 18), fontWeight: FontWeight.w500, color: const Color(0xFFF5F5DC), ), ), ], ), ), const SizedBox(height: 16), _buildRecentActivityGrid(constraints), ], ); } Widget _buildRecentActivityGrid(BoxConstraints constraints) { final isLargeScreen = constraints.maxWidth > 1200; final activities = statistics!.recentActivity.take(6); if (isLargeScreen) { return Wrap( spacing: 8, runSpacing: 8, children: activities .map( (activity) => SizedBox( width: (constraints.maxWidth - 48) / 3, child: _buildActivityCard(activity, constraints), ), ) .toList(), ); } else { return Wrap( spacing: 8, runSpacing: 8, children: activities .map((activity) => _buildActivityCard(activity, constraints)) .toList(), ); } } Widget _buildActivityCard( Map activity, BoxConstraints constraints, ) { final isLargeScreen = constraints.maxWidth > 1200; final isMediumScreen = constraints.maxWidth > 600; final cardWidth = isLargeScreen ? null : (constraints.maxWidth - 32) / 2 - 4; return MouseRegion( child: AnimatedContainer( duration: const Duration(milliseconds: 300), width: cardWidth, child: Card( elevation: 0, margin: EdgeInsets.zero, child: Container( padding: EdgeInsets.all(isMediumScreen ? 16 : 12), decoration: BoxDecoration( gradient: const LinearGradient( begin: Alignment.topLeft, end: Alignment.bottomRight, colors: [ Color.fromRGBO(160, 82, 45, 0.15), Color.fromRGBO(101, 67, 33, 0.1), ], ), borderRadius: BorderRadius.circular(8), border: Border.all( color: const Color.fromRGBO(210, 180, 140, 0.2), width: 1, ), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Icon( activity['type'] == 'Journal' ? Icons.book : Icons.coffee, size: isMediumScreen ? 16 : 14, color: const Color(0xFFD4A574), ), const SizedBox(width: 8), Container( padding: const EdgeInsets.symmetric( horizontal: 6, vertical: 2, ), decoration: BoxDecoration( borderRadius: BorderRadius.circular(4), border: Border.all(color: const Color(0xFFD4A574)), ), child: Text( activity['type'], style: TextStyle( fontSize: isLargeScreen ? 11 : (isMediumScreen ? 10 : 9), color: const Color(0xFFD4A574), ), ), ), ], ), const SizedBox(height: 8), Text( activity['description'], style: TextStyle( fontSize: isLargeScreen ? 14 : (isMediumScreen ? 12 : 11), color: const Color(0xFFF5F5DC), ), ), const SizedBox(height: 8), Text( _formatDate(activity['date']), style: TextStyle( fontSize: isLargeScreen ? 12 : (isMediumScreen ? 10 : 9), color: const Color(0xFFD2B48C), ), ), ], ), ), ), ), ); } Widget _buildCoffeeTipSection(BoxConstraints constraints) { final isLargeScreen = constraints.maxWidth > 1200; final isMediumScreen = constraints.maxWidth > 600; return Container( margin: const EdgeInsets.all(8), padding: EdgeInsets.all(isMediumScreen ? 24 : 16), decoration: const BoxDecoration( gradient: LinearGradient( begin: Alignment.topLeft, end: Alignment.bottomRight, colors: [Color(0xFF3C2414), Color(0xFF6F4E37)], ), borderRadius: BorderRadius.all(Radius.circular(8)), border: Border.fromBorderSide( BorderSide(color: Color.fromRGBO(212, 165, 116, 0.3), width: 1), ), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Icon( Icons.local_cafe, color: const Color(0xFFD4A574), size: isLargeScreen ? 32 : (isMediumScreen ? 28 : 24), ), const SizedBox(width: 8), Text( 'Coffee Tip of the Day', style: TextStyle( fontSize: isLargeScreen ? 22 : (isMediumScreen ? 18 : 16), fontWeight: FontWeight.w600, color: const Color(0xFFF5F5DC), ), ), ], ), const SizedBox(height: 16), Text( '"The perfect cup of coffee is a balance of quality beans, proper grind size, and optimal water temperature. Start your journey by exploring different origins and roast levels!"', style: TextStyle( fontSize: isLargeScreen ? 16 : (isMediumScreen ? 14 : 12), color: const Color(0xFFD2B48C), fontStyle: FontStyle.italic, height: 1.5, ), ), ], ), ); } String _formatDate(DateTime date) { const months = [ 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec', ]; return '${date.day} ${months[date.month - 1]} ${date.year}'; } }