Line data Source code
1 : import 'package:flutter/material.dart';
2 : import 'package:provider/provider.dart';
3 :
4 : import '../../providers/favorites_provider.dart';
5 : import '../../widgets/movie_card.dart';
6 :
7 : /// Displays the user's favorite movies in a grid layout.
8 : ///
9 : /// The `FavoritesPage` widget presents a list of movies that the user has marked as favorites.
10 : /// It manages several UI states:
11 : /// - **Loading**: Shows a loading indicator while favorites are being fetched.
12 : /// - **Error**: Displays an error message and a retry button if loading fails.
13 : /// - **Empty**: Informs the user when there are no favorites and encourages adding some.
14 : /// - **Content**: Renders a grid of favorite movies using [MovieCard] widgets.
15 : ///
16 : /// The page also provides an option to clear all favorites via an AppBar action, which triggers a confirmation dialog.
17 : ///
18 : /// ### Getters and Setters
19 : /// - The class itself does not define explicit getters/setters, but relies on [FavoritesProvider] for state management.
20 : /// - UI state and favorite movies are accessed through the provider's properties:
21 : /// - `favoriteMovies`: List of favorite movies.
22 : /// - `isLoading`: Indicates if the favorites are loading.
23 : /// - `error`: Error message if loading fails.
24 : /// - `isEmpty`: True if there are no favorites.
25 : ///
26 : /// ### Methods
27 : /// - [_showClearAllDialog]: Shows a confirmation dialog to clear all favorites.
28 : /// - [build]: Builds the widget tree for the page, handling all UI states.
29 : ///
30 : /// ### Usage
31 : /// Place this page in your navigation stack to allow users to view and manage their favorite movies.
32 : class FavoritesPage extends StatelessWidget {
33 : /// Creates a `FavoritesPage` instance.
34 14 : const FavoritesPage({super.key});
35 :
36 1 : @override
37 : /// Builds the widget tree for the favorites page.
38 : ///
39 : /// **Parameters:**
40 : /// - `context` (BuildContext): The build context.
41 : ///
42 : /// **Returns:**
43 : /// - A `Widget` representing the favorites page.
44 : Widget build(BuildContext context) {
45 1 : return Scaffold(
46 1 : appBar: AppBar(
47 : title: const Text('Favoritos'),
48 : automaticallyImplyLeading: false,
49 1 : actions: [
50 1 : Consumer<FavoritesProvider>(
51 1 : builder: (context, favoritesProvider, child) {
52 2 : if (favoritesProvider.favoriteMovies.isNotEmpty) {
53 1 : return IconButton(
54 : icon: const Icon(Icons.delete_sweep),
55 2 : onPressed: () => _showClearAllDialog(context),
56 : );
57 : }
58 : return const SizedBox.shrink();
59 : },
60 : ),
61 : ],
62 : ),
63 1 : body: Consumer<FavoritesProvider>(
64 1 : builder: (context, favoritesProvider, child) {
65 : // Loading state
66 1 : if (favoritesProvider.isLoading) {
67 : return const Center(child: CircularProgressIndicator());
68 : }
69 :
70 : // Error state
71 1 : if (favoritesProvider.error != null) {
72 1 : return Center(
73 1 : child: Column(
74 : mainAxisAlignment: MainAxisAlignment.center,
75 1 : children: [
76 2 : Icon(Icons.error_outline, size: 64, color: Colors.grey[400]),
77 : const SizedBox(height: 16),
78 1 : Text(
79 1 : favoritesProvider.error!,
80 : textAlign: TextAlign.center,
81 3 : style: Theme.of(context).textTheme.bodyLarge,
82 : ),
83 : const SizedBox(height: 16),
84 1 : ElevatedButton(
85 1 : onPressed: () {
86 1 : favoritesProvider.refreshFavorites();
87 : },
88 : child: const Text('Reintentar'),
89 : ),
90 : ],
91 : ),
92 : );
93 : }
94 :
95 : // Empty state
96 1 : if (favoritesProvider.isEmpty) {
97 1 : return Center(
98 1 : child: Column(
99 : mainAxisAlignment: MainAxisAlignment.center,
100 1 : children: [
101 1 : Icon(
102 : Icons.favorite_border,
103 : size: 64,
104 1 : color: Colors.grey[400],
105 : ),
106 : const SizedBox(height: 16),
107 1 : Text(
108 : 'No tienes favoritos aún',
109 1 : style: Theme.of(
110 : context,
111 4 : ).textTheme.titleLarge?.copyWith(color: Colors.grey[600]),
112 : ),
113 : const SizedBox(height: 8),
114 1 : Text(
115 : 'Explora películas y agrega las que más te gusten',
116 : textAlign: TextAlign.center,
117 3 : style: Theme.of(context).textTheme.bodyMedium,
118 : ),
119 : ],
120 : ),
121 : );
122 : }
123 :
124 : // Favorites grid
125 1 : return GridView.builder(
126 : padding: const EdgeInsets.all(16),
127 : gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
128 : crossAxisCount: 2,
129 : childAspectRatio: 0.7,
130 : crossAxisSpacing: 16,
131 : mainAxisSpacing: 16,
132 : ),
133 2 : itemCount: favoritesProvider.favoriteMovies.length,
134 1 : itemBuilder: (context, index) {
135 2 : final movie = favoritesProvider.favoriteMovies[index];
136 1 : return MovieCard(movie: movie);
137 : },
138 : );
139 : },
140 : ),
141 : );
142 : }
143 :
144 1 : void _showClearAllDialog(BuildContext context) {
145 1 : showDialog(
146 : context: context,
147 1 : builder: (BuildContext context) {
148 1 : return AlertDialog(
149 : title: const Text('Limpiar favoritos'),
150 : content: const Text(
151 : '¿Estás seguro de que quieres eliminar todos los favoritos?',
152 : ),
153 1 : actions: [
154 1 : TextButton(
155 3 : onPressed: () => Navigator.of(context).pop(),
156 : child: const Text('Cancelar'),
157 : ),
158 1 : TextButton(
159 1 : onPressed: () {
160 2 : context.read<FavoritesProvider>().clearAllFavorites();
161 2 : Navigator.of(context).pop();
162 : },
163 : child: const Text('Eliminar'),
164 : ),
165 : ],
166 : );
167 : },
168 : );
169 : }
170 : }
|