import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; import '../../shared/models/recipe.dart'; import 'recipe_provider.dart'; import 'widgets/recipe_card.dart'; import 'widgets/skeleton_card.dart'; class RecommendationsScreen extends ConsumerWidget { const RecommendationsScreen({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final state = ref.watch(recommendationsProvider); return Scaffold( // AppBar is owned by RecipesScreen (tab host), but we add the // refresh action via a floating action button inside this child. body: state.when( loading: () => _SkeletonList(), error: (err, _) => _ErrorView( message: err.toString(), onRetry: () => ref.read(recommendationsProvider.notifier).load(), ), data: (recipes) => _RecipeList(recipes: recipes), ), floatingActionButton: FloatingActionButton( heroTag: 'refresh_recommendations', tooltip: 'Обновить рекомендации', onPressed: state is AsyncLoading ? null : () => ref.read(recommendationsProvider.notifier).load(), child: state is AsyncLoading ? const SizedBox( width: 24, height: 24, child: CircularProgressIndicator(strokeWidth: 2), ) : const Icon(Icons.refresh), ), ); } } // --------------------------------------------------------------------------- // Skeleton list — shown while AI is generating recipes // --------------------------------------------------------------------------- class _SkeletonList extends StatelessWidget { @override Widget build(BuildContext context) { return ListView.separated( padding: const EdgeInsets.all(16), itemCount: 3, separatorBuilder: (_, __) => const SizedBox(height: 12), itemBuilder: (_, __) => const SkeletonCard(), ); } } // --------------------------------------------------------------------------- // Loaded recipe list // --------------------------------------------------------------------------- class _RecipeList extends StatelessWidget { final List recipes; const _RecipeList({required this.recipes}); @override Widget build(BuildContext context) { if (recipes.isEmpty) { return const Center( child: Text('Нет рекомендаций. Нажмите ↻ чтобы получить рецепты.'), ); } return ListView.separated( padding: const EdgeInsets.fromLTRB(16, 16, 16, 88), // room for FAB itemCount: recipes.length, separatorBuilder: (_, __) => const SizedBox(height: 12), itemBuilder: (context, index) { final recipe = recipes[index]; return RecipeCard( recipe: recipe, onTap: () => context.push( '/recipe-detail', extra: recipe, ), ); }, ); } } // --------------------------------------------------------------------------- // Error view // --------------------------------------------------------------------------- class _ErrorView extends StatelessWidget { final String message; final VoidCallback onRetry; const _ErrorView({required this.message, required this.onRetry}); @override Widget build(BuildContext context) { return Center( child: Padding( padding: const EdgeInsets.all(24), child: Column( mainAxisSize: MainAxisSize.min, children: [ const Icon(Icons.error_outline, size: 48, color: Colors.red), const SizedBox(height: 12), const Text( 'Не удалось получить рецепты', style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold), textAlign: TextAlign.center, ), const SizedBox(height: 8), Text( message, style: const TextStyle(color: Colors.grey), textAlign: TextAlign.center, maxLines: 3, overflow: TextOverflow.ellipsis, ), const SizedBox(height: 20), ElevatedButton.icon( onPressed: onRetry, icon: const Icon(Icons.refresh), label: const Text('Попробовать снова'), ), ], ), ), ); } }