import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; import '../../../core/api/api_error_mapper.dart'; import '../../../core/l10n/l10n.dart'; import '../../../core/ui/async_state_views.dart'; import '../data/recipe_providers.dart'; import '../data/recipes_grid_provider.dart'; import '../domain/recipe.dart'; class RecipesScreen extends ConsumerWidget { const RecipesScreen({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final recipesAsync = ref.watch(recipesProvider); final view = ref.watch(recipesViewProvider).maybeWhen( data: (v) => v, orElse: () => (mode: RecipesViewMode.grid, columns: 2), ); return Stack( children: [ recipesAsync.when( loading: () => LoadingStateView(label: context.l10n.recipesLoading), error: (error, _) => ErrorStateView( message: mapErrorToUserMessage(error, context), onRetry: () => ref.invalidate(recipesProvider), ), data: (recipes) { if (recipes.isEmpty) { return EmptyStateView( title: context.l10n.recipesEmpty, description: context.l10n.recipesEmptyDescription, ); } if (view.mode == RecipesViewMode.grid) { return GridView.builder( padding: const EdgeInsets.only(bottom: 88), gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: view.columns, crossAxisSpacing: 4, mainAxisSpacing: 4, ), itemCount: recipes.length, itemBuilder: (context, index) { final recipe = recipes[index]; return GestureDetector( onTap: () => context.push('/recipes/${recipe.id}'), child: _RecipeImageCard(recipe: recipe), ); }, ); } else { return ListView.builder( padding: const EdgeInsets.only(bottom: 88), itemCount: recipes.length, itemBuilder: (context, index) { final recipe = recipes[index]; return InkWell( onTap: () => context.push('/recipes/${recipe.id}'), child: Padding( padding: const EdgeInsets.symmetric( horizontal: 12, vertical: 6), child: Row( children: [ ClipRRect( borderRadius: BorderRadius.circular(6), child: SizedBox( width: 72, height: 72, child: _RecipeImageCard(recipe: recipe, compact: true), ), ), const SizedBox(width: 12), Expanded( child: Text( recipe.title, style: Theme.of(context).textTheme.titleMedium, maxLines: 2, overflow: TextOverflow.ellipsis, ), ), ], ), ), ); }, ); } }, ), Positioned( right: 16, bottom: 16, child: FloatingActionButton( tooltip: context.l10n.recipesNewTooltip, onPressed: () => context.push('/recipes/create'), child: const Icon(Icons.add), ), ), ], ); } } class _RecipeImageCard extends StatelessWidget { final Recipe recipe; final bool compact; const _RecipeImageCard({required this.recipe, this.compact = false}); @override Widget build(BuildContext context) { final showStamp = recipe.isPublic == true && recipe.ownerUsername != null && recipe.ownerUsername.toString().isNotEmpty; final radius = compact ? 0.0 : 8.0; return Container( decoration: BoxDecoration( borderRadius: BorderRadius.circular(radius), color: Colors.grey[200], image: recipe.imageUrl != null ? DecorationImage( image: NetworkImage(recipe.imageUrl!), fit: BoxFit.cover, ) : null, ), child: Stack( children: [ if (recipe.imageUrl == null) const Center(child: Icon(Icons.restaurant, size: 32)), if (showStamp) Positioned( right: 4, bottom: 4, child: Container( padding: const EdgeInsets.symmetric(horizontal: 5, vertical: 2), decoration: BoxDecoration( color: Colors.black.withOpacity(0.45), borderRadius: BorderRadius.circular(10), ), child: Text( '@${recipe.ownerUsername}', style: TextStyle( color: Colors.white, fontSize: compact ? 8 : 10, fontWeight: FontWeight.w600, ), ), ), ), ], ), ); } }