@@ -18,6 +18,7 @@ class _ArtistDetailPageState extends ConsumerState<ArtistDetailPage> {
1818 @override
1919 Widget build (BuildContext context) {
2020 final artistDetailAsync = ref.watch (artistDetailProvider (widget.artistId));
21+ final artistAllMoviesAsync = ref.watch (artistDetailAllMoviesProvider (widget.artistId));
2122
2223 return Scaffold (
2324 backgroundColor: Colors .white,
@@ -34,9 +35,7 @@ class _ArtistDetailPageState extends ConsumerState<ArtistDetailPage> {
3435 expandedHeight: 0 ,
3536 leading: IconButton (
3637 icon: const Icon (Icons .arrow_back),
37- onPressed: () => {
38- Navigator .pop (context)
39- },
38+ onPressed: () => Navigator .pop (context),
4039 ),
4140 title: Text (
4241 artist.name,
@@ -68,13 +67,10 @@ class _ArtistDetailPageState extends ConsumerState<ArtistDetailPage> {
6867 fit: BoxFit .cover,
6968 )
7069 : null ,
71- color: artist.profilePath == null
72- ? Colors .grey[300 ]
73- : null ,
70+ color: artist.profilePath == null ? Colors .grey[300 ] : null ,
7471 ),
7572 child: artist.profilePath == null
76- ? const Icon (Icons .person,
77- color: Colors .grey, size: 60 )
73+ ? const Icon (Icons .person, color: Colors .grey, size: 60 )
7874 : null ,
7975 ),
8076 const SizedBox (width: 18 ),
@@ -93,68 +89,29 @@ class _ArtistDetailPageState extends ConsumerState<ArtistDetailPage> {
9389 const SizedBox (height: 8 ),
9490 Text (
9591 'Artist Detail' ,
96- style: TextStyle (
97- fontSize: 12 ,
98- color: Colors .grey[600 ],
99- ),
92+ style: TextStyle (fontSize: 12 , color: Colors .grey[600 ]),
10093 ),
10194 Text (
10295 artist.knownForDepartment ?? 'Acting' ,
103- style: const TextStyle (
104- fontSize: 16 ,
105- color: Colors .black87,
106- ),
96+ style: const TextStyle (fontSize: 16 , color: Colors .black87),
10797 ),
10898 const SizedBox (height: 8 ),
109-
110- // Artist Detail Info
111- Text (
112- 'Artist Detail' ,
113- style: TextStyle (
114- fontSize: 12 ,
115- color: Colors .grey[600 ],
116- ),
117- ),
118- const Text (
119- '1' ,
120- style: TextStyle (
121- fontSize: 16 ,
122- color: Colors .black87,
123- ),
124- ),
125- const SizedBox (height: 8 ),
126-
127- // Birthday
12899 Text (
129100 'Birthday' ,
130- style: TextStyle (
131- fontSize: 12 ,
132- color: Colors .grey[600 ],
133- ),
101+ style: TextStyle (fontSize: 12 , color: Colors .grey[600 ]),
134102 ),
135103 Text (
136- artist.birthday ?? '1978-10-13' ,
137- style: const TextStyle (
138- fontSize: 16 ,
139- color: Colors .black87,
140- ),
104+ artist.birthday ?? 'N/A' ,
105+ style: const TextStyle (fontSize: 16 , color: Colors .black87),
141106 ),
142107 const SizedBox (height: 8 ),
143-
144- // Place of Birth
145108 Text (
146109 'Place of Birth' ,
147- style: TextStyle (
148- fontSize: 12 ,
149- color: Colors .grey[600 ],
150- ),
110+ style: TextStyle (fontSize: 12 , color: Colors .grey[600 ]),
151111 ),
152112 Text (
153- artist.placeOfBirth ?? 'Orange County, California, USA' ,
154- style: const TextStyle (
155- fontSize: 16 ,
156- color: Colors .black87,
157- ),
113+ artist.placeOfBirth ?? 'N/A' ,
114+ style: const TextStyle (fontSize: 16 , color: Colors .black87),
158115 ),
159116 ],
160117 ),
@@ -166,24 +123,14 @@ class _ArtistDetailPageState extends ConsumerState<ArtistDetailPage> {
166123 // Biography Section
167124 const Text (
168125 'Biography' ,
169- style: TextStyle (
170- fontSize: 24 ,
171- fontWeight: FontWeight .bold,
172- ),
126+ style: TextStyle (fontSize: 24 , fontWeight: FontWeight .bold),
173127 ),
174128 const SizedBox (height: 12 ),
175129 Text (
176- artist.biography ??
177- "Sadie Katz is a working actress living in Los Angeles. Her most recent film Credits include starring in 20th Century Fox's Fan Favorite Horror franchise \" Wrong Turn 6\" playing the twisted, sexy Sally Hillicker. Showing her range and acting chops she also played the sweet sensitive, leading lady in \" Chavez: Cage of Glory\" opening in 400 theaters September 2013 along side Danny Trejo, Steven Bauer and Hector Echavarria. Katz also stars in the thriller \" House of Bad\" Fan Favorite Award Winner at Big Bear Horror Fest 2013. You can also see Sadie starring as a free-spirited partygirl with issues in \" Nipple and Pal..." ,
130+ artist.biography ?? 'No biography available.' ,
178131 maxLines: _isExpanded ? null : 4 ,
179- overflow: _isExpanded
180- ? TextOverflow .visible
181- : TextOverflow .ellipsis,
182- style: const TextStyle (
183- fontSize: 16 ,
184- height: 1.5 ,
185- color: Colors .black87,
186- ),
132+ overflow: _isExpanded ? TextOverflow .visible : TextOverflow .ellipsis,
133+ style: const TextStyle (fontSize: 16 , height: 1.5 , color: Colors .black87),
187134 ),
188135 const SizedBox (height: 8 ),
189136 GestureDetector (
@@ -202,6 +149,9 @@ class _ArtistDetailPageState extends ConsumerState<ArtistDetailPage> {
202149 ),
203150 ),
204151 const SizedBox (height: 20 ),
152+
153+ // Artist Movies Section
154+ ArtistMoviesSection (artistAllMoviesAsync: artistAllMoviesAsync),
205155 ],
206156 ),
207157 ),
@@ -234,4 +184,87 @@ class _ArtistDetailPageState extends ConsumerState<ArtistDetailPage> {
234184 ),
235185 );
236186 }
187+ }
188+
189+ class ArtistMoviesSection extends StatelessWidget {
190+ final AsyncValue <dynamic > artistAllMoviesAsync; // expecting API response
191+
192+ const ArtistMoviesSection ({super .key, required this .artistAllMoviesAsync});
193+
194+ @override
195+ Widget build (BuildContext context) {
196+ return artistAllMoviesAsync.when (
197+ data: (moviesResponse) {
198+ final movies = moviesResponse.cast; // cast list from API
199+ if (movies.isEmpty) return const SizedBox .shrink ();
200+
201+ return Column (
202+ crossAxisAlignment: CrossAxisAlignment .start,
203+ children: [
204+ const Padding (
205+ padding: EdgeInsets .fromLTRB (0 , 12 , 0 , 0 ),
206+ child: Text (
207+ 'Movies' ,
208+ style: TextStyle (
209+ fontSize: 20 ,
210+ fontWeight: FontWeight .bold,
211+ color: Colors .black87,
212+ ),
213+ ),
214+ ),
215+ const SizedBox (height: 12 ),
216+ SizedBox (
217+ height: 160 ,
218+ child: ListView .separated (
219+ scrollDirection: Axis .horizontal,
220+ itemCount: movies.length,
221+ separatorBuilder: (_, __) => const SizedBox (width: 12 ),
222+ itemBuilder: (context, index) {
223+ final movie = movies[index];
224+ return GestureDetector (
225+ onTap: () {
226+ Navigator .pushNamed (context, '/movie/${movie .id }' );
227+ },
228+ child: ClipRRect (
229+ borderRadius: BorderRadius .circular (12 ),
230+ child: movie.posterPath != null
231+ ? Image .network (
232+ '$IMAGE_URL ${movie .posterPath }' ,
233+ width: 110 ,
234+ height: 160 ,
235+ fit: BoxFit .cover,
236+ errorBuilder: (_, __, ___) => _errorPlaceholder (),
237+ )
238+ : _errorPlaceholder (),
239+ ),
240+ );
241+ },
242+ ),
243+ ),
244+ const SizedBox (height: 12 ),
245+ ],
246+ );
247+ },
248+ loading: () => const Padding (
249+ padding: EdgeInsets .symmetric (vertical: 24 ),
250+ child: Center (child: CircularProgressIndicator ()),
251+ ),
252+ error: (error, _) => Padding (
253+ padding: const EdgeInsets .symmetric (vertical: 8 ),
254+ child: Text (
255+ 'Failed to load movies.' ,
256+ style: TextStyle (color: Colors .red[400 ]),
257+ ),
258+ ),
259+ );
260+ }
261+
262+ Widget _errorPlaceholder () {
263+ return Container (
264+ width: 100 ,
265+ height: 160 ,
266+ color: Colors .grey[300 ],
267+ child: const Icon (Icons .movie, size: 48 , color: Colors .grey),
268+ );
269+ }
237270}
0 commit comments