import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import '../providers/app_state.dart'; import '../models/recipe.dart'; import '../components/recipe_dialog.dart'; import '../components/searchable_selection.dart'; class RecipesScreen extends StatelessWidget { const RecipesScreen({super.key}); @override Widget build(BuildContext context) { return Consumer( builder: (context, appState, child) { if (appState.isLoading) { return const Center(child: CircularProgressIndicator()); } return Scaffold( body: appState.recipes.isEmpty ? const Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon(Icons.menu_book, size: 64, color: Colors.grey), SizedBox(height: 16), Text('No recipes in your collection yet'), Text('Tap the + button to browse and add recipes'), ], ), ) : ListView.builder( padding: const EdgeInsets.all(16), itemCount: appState.recipes.length, itemBuilder: (context, index) { final recipe = appState.recipes[index]; return _buildRecipeCard(context, recipe); }, ), floatingActionButton: FloatingActionButton( onPressed: () => _showAddRecipeDialog(context), child: const Icon(Icons.add), ), ); }, ); } Widget _buildRecipeCard(BuildContext context, Recipe recipe) { return Card( margin: const EdgeInsets.only(bottom: 16), child: Padding( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Expanded( child: Text(recipe.name, style: Theme.of(context).textTheme.headlineSmall), ), PopupMenuButton( itemBuilder: (context) => [ const PopupMenuItem( value: 'edit', child: Row( children: [ Icon(Icons.edit), SizedBox(width: 8), Text('Edit'), ], ), ), const PopupMenuItem( value: 'delete', child: Row( children: [ Icon(Icons.delete, color: Colors.red), SizedBox(width: 8), Text('Delete', style: TextStyle(color: Colors.red)), ], ), ), ], onSelected: (value) { if (value == 'edit') { _showEditRecipeDialog(context, recipe); } else if (value == 'delete') { _showDeleteRecipeDialog(context, recipe); } }, ), ], ), const SizedBox(height: 8), Row( children: [ Icon(_getBrewMethodIcon(recipe.brewMethod), size: 16), const SizedBox(width: 4), Text(recipe.brewMethod.name), const SizedBox(width: 16), Icon(_getServingTempIcon(recipe.servingTemp), size: 16), const SizedBox(width: 4), Text(recipe.servingTemp.name), ], ), const SizedBox(height: 8), Row( children: [ Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('Coffee: ${recipe.coffeeAmount}g'), Text('Water: ${recipe.waterAmount}ml'), ], ), ), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('Grind: ${recipe.grindSize}'), Text('Time: ${_formatBrewTime(recipe.brewTime)}'), ], ), ), ], ), if (recipe.milkType != null) ...[ const SizedBox(height: 4), Text('Milk: ${recipe.milkType!.name}'), ], if (recipe.notes != null) ...[ const SizedBox(height: 8), Text( recipe.notes!, style: Theme.of(context).textTheme.bodyMedium, ), ], const SizedBox(height: 12), Text( 'Instructions:', style: Theme.of(context).textTheme.titleMedium, ), const SizedBox(height: 4), Text( recipe.instructions, style: Theme.of(context).textTheme.bodyMedium, ), ], ), ), ); } IconData _getBrewMethodIcon(BrewMethod method) { switch (method) { case BrewMethod.espresso: return Icons.local_cafe; case BrewMethod.drip: return Icons.water_drop; case BrewMethod.frenchPress: return Icons.coffee_maker; case BrewMethod.pourOver: return Icons.filter_alt; } } IconData _getServingTempIcon(ServingTemp temp) { switch (temp) { case ServingTemp.hot: return Icons.local_fire_department; case ServingTemp.cold: return Icons.ac_unit; case ServingTemp.iced: return Icons.icecream; } } String _formatBrewTime(int seconds) { final minutes = seconds ~/ 60; final remainingSeconds = seconds % 60; if (minutes > 0) { return '${minutes}m ${remainingSeconds}s'; } return '${seconds}s'; } void _showAddRecipeDialog(BuildContext context) { _showRecipeCatalog(context); } void _showRecipeCatalog(BuildContext context) async { final appState = Provider.of(context, listen: false); // Get all available recipes from catalog final availableRecipes = await appState.getAllAvailableRecipes(); if (availableRecipes.isEmpty) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('No recipes available in catalog')), ); return; } Navigator.of(context).push( MaterialPageRoute( builder: (context) => SearchableSelection( items: availableRecipes, title: 'Browse Recipe Catalog', searchHint: 'Search recipes...', displayText: (recipe) => recipe.name, onItemSelected: (recipe) async { Navigator.of(context).pop(); // Check if recipe is already in user's collection if (appState.recipes.any((r) => r.id == recipe.id)) { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text('${recipe.name} is already in your collection')), ); return; } // Add recipe to user's collection try { await appState.addRecipe(recipe); ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text('${recipe.name} added to your collection!')), ); } catch (e) { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text('Error adding recipe: $e')), ); } }, onAddCustom: () { Navigator.of(context).pop(); // Close the search screen showDialog( context: context, builder: (context) => const RecipeDialog(), ); }, ), ), ); } void _showEditRecipeDialog(BuildContext context, Recipe recipe) { showDialog( context: context, builder: (context) => RecipeDialog(recipe: recipe), ); } void _showDeleteRecipeDialog(BuildContext context, Recipe recipe) { showDialog( context: context, builder: (context) => AlertDialog( title: const Text('Delete Recipe'), content: Text('Are you sure you want to delete "${recipe.name}"?'), actions: [ TextButton( onPressed: () => Navigator.of(context).pop(), child: const Text('Cancel'), ), ElevatedButton( onPressed: () async { try { await Provider.of(context, listen: false) .deleteRecipe(recipe.id); if (context.mounted) { Navigator.of(context).pop(); ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Recipe deleted successfully!')), ); } } catch (e) { if (context.mounted) { Navigator.of(context).pop(); ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text('Error deleting recipe: $e')), ); } } }, style: ElevatedButton.styleFrom(backgroundColor: Colors.red), child: const Text('Delete'), ), ], ), ); } }