Line data Source code
1 : import 'package:flutter/material.dart';
2 : import 'package:skeleton_loader/skeleton_loader.dart';
3 :
4 : /// Displays a grid of skeleton loaders to indicate loading state for movie cards.
5 : ///
6 : /// The `LoadingGrid` widget is used to show a placeholder grid while movie data is being fetched.
7 : /// It renders a configurable number of skeleton cards, matching the layout of the movie grid.
8 : ///
9 : /// ### Parameters
10 : /// - [itemCount]: Number of skeleton items to display (default: 6).
11 : /// - [childAspectRatio]: Aspect ratio for each grid item (default: 0.7).
12 : ///
13 : /// ### Usage
14 : /// Use this widget in place of a movie grid when data is loading to provide a smooth user experience.
15 : class LoadingGrid extends StatelessWidget {
16 : final int itemCount;
17 : final double childAspectRatio;
18 :
19 14 : const LoadingGrid({
20 : super.key,
21 : this.itemCount = 6,
22 : this.childAspectRatio = 0.7,
23 : });
24 :
25 2 : @override
26 : Widget build(BuildContext context) {
27 2 : return GridView.builder(
28 : padding: const EdgeInsets.all(16),
29 2 : gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
30 : crossAxisCount: 2,
31 2 : childAspectRatio: childAspectRatio,
32 : crossAxisSpacing: 16,
33 : mainAxisSpacing: 16,
34 : ),
35 2 : itemCount: itemCount,
36 2 : itemBuilder: (context, index) {
37 : return const MovieCardSkeleton();
38 : },
39 : );
40 : }
41 : }
42 :
43 : /// Displays a skeleton loader for a single movie card, simulating a loading state in grids.
44 : ///
45 : /// The `MovieCardSkeleton` widget is used to show a placeholder for a movie card while data is being fetched.
46 : /// It mimics the layout of a typical movie card, including poster and info placeholders.
47 : ///
48 : /// ### Usage
49 : /// Use this widget in grids to provide a smooth loading experience while movie data is loading.
50 : class MovieCardSkeleton extends StatelessWidget {
51 14 : const MovieCardSkeleton({super.key});
52 :
53 2 : @override
54 : Widget build(BuildContext context) {
55 2 : return Card(
56 : elevation: 4,
57 4 : shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
58 2 : child: SkeletonLoader(
59 2 : builder: Container(
60 : height: 280, // Altura fija para evitar overflow
61 4 : decoration: BoxDecoration(borderRadius: BorderRadius.circular(12)),
62 2 : child: Column(
63 : crossAxisAlignment: CrossAxisAlignment.start,
64 2 : children: [
65 : // Poster placeholder
66 2 : Expanded(
67 : flex: 3,
68 2 : child: Container(
69 : width: double.infinity,
70 : decoration: const BoxDecoration(
71 : color: Colors.grey,
72 : borderRadius: BorderRadius.vertical(
73 : top: Radius.circular(12),
74 : ),
75 : ),
76 : ),
77 : ),
78 :
79 : // Info placeholder
80 2 : Expanded(
81 : flex: 1,
82 2 : child: Padding(
83 : padding: const EdgeInsets.all(8.0),
84 2 : child: Column(
85 : crossAxisAlignment: CrossAxisAlignment.start,
86 2 : children: [
87 : // Title placeholder
88 2 : Flexible(
89 2 : child: Container(
90 : height: 14,
91 : width: double.infinity,
92 2 : decoration: BoxDecoration(
93 : color: Colors.grey,
94 2 : borderRadius: BorderRadius.circular(4),
95 : ),
96 : ),
97 : ),
98 : const SizedBox(height: 4),
99 :
100 : // Subtitle placeholder
101 2 : Container(
102 : height: 10,
103 : width: 60,
104 2 : decoration: BoxDecoration(
105 : color: Colors.grey,
106 2 : borderRadius: BorderRadius.circular(4),
107 : ),
108 : ),
109 : ],
110 : ),
111 : ),
112 : ),
113 : ],
114 : ),
115 : ),
116 : items: 1,
117 : period: const Duration(seconds: 2),
118 2 : highlightColor: Colors.white.withValues(alpha: 0.6),
119 : direction: SkeletonDirection.ltr,
120 : ),
121 : );
122 : }
123 : }
124 :
125 : /// Displays a skeleton loader for a single list item, simulating a loading state for movie rows.
126 : ///
127 : /// The `LoadingListItem` widget is used to show a placeholder for a movie list item while data is being fetched.
128 : /// It mimics the layout of a typical movie row, including poster, title, subtitle, and rating placeholders.
129 : ///
130 : /// ### Usage
131 : /// Use this widget in place of a movie list item when data is loading to provide a smooth user experience.
132 : class LoadingListItem extends StatelessWidget {
133 1 : const LoadingListItem({super.key});
134 :
135 1 : @override
136 : Widget build(BuildContext context) {
137 1 : return Card(
138 : margin: const EdgeInsets.only(bottom: 16),
139 1 : child: SkeletonLoader(
140 1 : builder: Container(
141 : padding: const EdgeInsets.all(16),
142 1 : child: Row(
143 1 : children: [
144 : // Poster placeholder
145 1 : Container(
146 : width: 60,
147 : height: 90,
148 1 : decoration: BoxDecoration(
149 : color: Colors.grey,
150 1 : borderRadius: BorderRadius.circular(8),
151 : ),
152 : ),
153 : const SizedBox(width: 16),
154 :
155 : // Content placeholder
156 1 : Expanded(
157 1 : child: Column(
158 : crossAxisAlignment: CrossAxisAlignment.start,
159 1 : children: [
160 : // Title placeholder
161 1 : Container(
162 : height: 16,
163 : width: double.infinity,
164 1 : decoration: BoxDecoration(
165 : color: Colors.grey,
166 1 : borderRadius: BorderRadius.circular(4),
167 : ),
168 : ),
169 : const SizedBox(height: 8),
170 :
171 : // Subtitle placeholder
172 1 : Container(
173 : height: 12,
174 : width: 100,
175 1 : decoration: BoxDecoration(
176 : color: Colors.grey,
177 1 : borderRadius: BorderRadius.circular(4),
178 : ),
179 : ),
180 : const SizedBox(height: 8),
181 :
182 : // Rating placeholder
183 1 : Container(
184 : height: 12,
185 : width: 60,
186 1 : decoration: BoxDecoration(
187 : color: Colors.grey,
188 1 : borderRadius: BorderRadius.circular(4),
189 : ),
190 : ),
191 : ],
192 : ),
193 : ),
194 : ],
195 : ),
196 : ),
197 : items: 1,
198 : period: const Duration(seconds: 2),
199 1 : highlightColor: Colors.white.withValues(alpha: 0.6),
200 : direction: SkeletonDirection.ltr,
201 : ),
202 : );
203 : }
204 : }
|