1+ import 'package:flutter/material.dart' ;
2+
3+ class DetailsPage extends StatelessWidget {
4+ const DetailsPage ({super .key});
5+
6+ @override
7+ Widget build (BuildContext context) {
8+ return Scaffold (
9+ backgroundColor: const Color (0xFFF9F3FF ),
10+ body: SafeArea (
11+ child: SingleChildScrollView (
12+ child: Column (
13+ crossAxisAlignment: CrossAxisAlignment .start,
14+ children: [
15+ Stack (
16+ children: [
17+ Container (
18+ height: 320 ,
19+ width: double .infinity,
20+ decoration: const BoxDecoration (
21+ borderRadius: BorderRadius .only (
22+ bottomLeft: Radius .circular (32 ),
23+ bottomRight: Radius .circular (32 ),
24+ ),
25+ image: DecorationImage (
26+ image: NetworkImage ('https://image.tmdb.org/t/p/w500/ochi.jpg' ),
27+ fit: BoxFit .cover,
28+ ),
29+ ),
30+ ),
31+ Positioned (
32+ top: 16 ,
33+ left: 16 ,
34+ child: _CircleButton (
35+ icon: Icons .arrow_back,
36+ onTap: () => Navigator .of (context).pop (),
37+ ),
38+ ),
39+ Positioned (
40+ top: 16 ,
41+ right: 16 ,
42+ child: _CircleButton (
43+ icon: Icons .favorite_border,
44+ onTap: () {},
45+ ),
46+ ),
47+ Positioned (
48+ left: 24 ,
49+ bottom: 0 ,
50+ child: _RatingIndicator (percent: 0.58 ),
51+ ),
52+ Positioned (
53+ left: 100 ,
54+ bottom: 32 ,
55+ right: 16 ,
56+ child: Column (
57+ crossAxisAlignment: CrossAxisAlignment .start,
58+ children: const [
59+ Text (
60+ 'The Legend of Ochi' ,
61+ style: TextStyle (
62+ color: Colors .black,
63+ fontSize: 24 ,
64+ fontWeight: FontWeight .bold,
65+ ),
66+ ),
67+ SizedBox (height: 4 ),
68+ Text (
69+ '-' ,
70+ style: TextStyle (
71+ color: Colors .black54,
72+ fontSize: 16 ,
73+ ),
74+ ),
75+ ],
76+ ),
77+ ),
78+ ],
79+ ),
80+ const SizedBox (height: 24 ),
81+ const Padding (
82+ padding: EdgeInsets .symmetric (horizontal: 16.0 ),
83+ child: Text (
84+ 'In a remote village on the island of Carpathia, a shy farm girl named Yuri is raised to fear an elusive animal species known as ochi. But when Yuri discovers a wounded baby ochi has been left behind, she escapes on a quest to bring him home.' ,
85+ style: TextStyle (
86+ color: Colors .black87,
87+ fontSize: 16 ,
88+ ),
89+ ),
90+ ),
91+ const SizedBox (height: 24 ),
92+ const Padding (
93+ padding: EdgeInsets .symmetric (horizontal: 16.0 ),
94+ child: Text (
95+ 'Cast' ,
96+ style: TextStyle (
97+ color: Colors .black,
98+ fontSize: 18 ,
99+ fontWeight: FontWeight .bold,
100+ ),
101+ ),
102+ ),
103+ const SizedBox (height: 12 ),
104+ SizedBox (
105+ height: 90 ,
106+ child: ListView (
107+ scrollDirection: Axis .horizontal,
108+ padding: const EdgeInsets .symmetric (horizontal: 16.0 ),
109+ children: const [
110+ _CastCard (name: 'Helena Zengel' , image: 'https://randomuser.me/api/portraits/women/1.jpg' ),
111+ _CastCard (name: 'Finn Wolfhard' , image: 'https://randomuser.me/api/portraits/men/2.jpg' ),
112+ _CastCard (name: 'Emily Watson' , image: 'https://randomuser.me/api/portraits/women/3.jpg' ),
113+ _CastCard (name: 'Willem Dafoe' , image: 'https://randomuser.me/api/portraits/men/4.jpg' ),
114+ _CastCard (name: 'Razvan Stoica' , image: 'https://randomuser.me/api/portraits/men/5.jpg' ),
115+ _CastCard (name: 'Carol B.' , image: 'https://randomuser.me/api/portraits/women/6.jpg' ),
116+ ],
117+ ),
118+ ),
119+ const SizedBox (height: 24 ),
120+ const Padding (
121+ padding: EdgeInsets .symmetric (horizontal: 16.0 ),
122+ child: Text (
123+ 'Categories' ,
124+ style: TextStyle (
125+ color: Colors .black,
126+ fontSize: 16 ,
127+ fontWeight: FontWeight .bold,
128+ ),
129+ ),
130+ ),
131+ const SizedBox (height: 12 ),
132+ Padding (
133+ padding: const EdgeInsets .symmetric (horizontal: 16.0 ),
134+ child: Wrap (
135+ spacing: 8 ,
136+ children: const [
137+ _CategoryChip (label: 'Fantasy' ),
138+ _CategoryChip (label: 'Adventure' ),
139+ _CategoryChip (label: 'Family' ),
140+ ],
141+ ),
142+ ),
143+ const SizedBox (height: 24 ),
144+ const Padding (
145+ padding: EdgeInsets .symmetric (horizontal: 16.0 ),
146+ child: Text (
147+ 'Recommendations' ,
148+ style: TextStyle (
149+ color: Colors .black,
150+ fontSize: 18 ,
151+ fontWeight: FontWeight .bold,
152+ ),
153+ ),
154+ ),
155+ const SizedBox (height: 12 ),
156+ SizedBox (
157+ height: 180 ,
158+ child: ListView (
159+ scrollDirection: Axis .horizontal,
160+ padding: const EdgeInsets .symmetric (horizontal: 16.0 ),
161+ children: const [
162+ _RecommendationCard (image: 'https://image.tmdb.org/t/p/w500/rec1.jpg' ),
163+ _RecommendationCard (image: 'https://image.tmdb.org/t/p/w500/rec2.jpg' ),
164+ _RecommendationCard (image: 'https://image.tmdb.org/t/p/w500/rec3.jpg' ),
165+ ],
166+ ),
167+ ),
168+ const SizedBox (height: 32 ),
169+ ],
170+ ),
171+ ),
172+ ),
173+ );
174+ }
175+ }
176+
177+ class _CircleButton extends StatelessWidget {
178+ final IconData icon;
179+ final VoidCallback onTap;
180+ const _CircleButton ({required this .icon, required this .onTap});
181+
182+ @override
183+ Widget build (BuildContext context) {
184+ return Material (
185+ color: Colors .white.withOpacity (0.7 ),
186+ shape: const CircleBorder (),
187+ child: InkWell (
188+ customBorder: const CircleBorder (),
189+ onTap: onTap,
190+ child: Padding (
191+ padding: const EdgeInsets .all (10.0 ),
192+ child: Icon (icon, color: Colors .black, size: 24 ),
193+ ),
194+ ),
195+ );
196+ }
197+ }
198+
199+ class _RatingIndicator extends StatelessWidget {
200+ final double percent;
201+ const _RatingIndicator ({required this .percent});
202+
203+ @override
204+ Widget build (BuildContext context) {
205+ return Column (
206+ children: [
207+ SizedBox (
208+ width: 48 ,
209+ height: 48 ,
210+ child: Stack (
211+ fit: StackFit .expand,
212+ children: [
213+ CircularProgressIndicator (
214+ value: percent,
215+ strokeWidth: 5 ,
216+ backgroundColor: Colors .grey[300 ],
217+ valueColor: const AlwaysStoppedAnimation <Color >(Color (0xFFB388F6 )),
218+ ),
219+ Center (
220+ child: Text (
221+ '${(percent * 100 ).round ()}%' ,
222+ style: const TextStyle (
223+ color: Color (0xFF7C4DFF ),
224+ fontWeight: FontWeight .bold,
225+ ),
226+ ),
227+ ),
228+ ],
229+ ),
230+ ),
231+ ],
232+ );
233+ }
234+ }
235+
236+ class _CastCard extends StatelessWidget {
237+ final String name;
238+ final String image;
239+ const _CastCard ({required this .name, required this .image});
240+
241+ @override
242+ Widget build (BuildContext context) {
243+ return Container (
244+ width: 70 ,
245+ margin: const EdgeInsets .only (right: 12 ),
246+ child: Column (
247+ children: [
248+ CircleAvatar (
249+ radius: 28 ,
250+ backgroundImage: NetworkImage (image),
251+ ),
252+ const SizedBox (height: 4 ),
253+ Text (
254+ name,
255+ style: const TextStyle (
256+ color: Colors .black87,
257+ fontSize: 12 ,
258+ fontWeight: FontWeight .w500,
259+ ),
260+ maxLines: 1 ,
261+ overflow: TextOverflow .ellipsis,
262+ textAlign: TextAlign .center,
263+ ),
264+ ],
265+ ),
266+ );
267+ }
268+ }
269+
270+ class _CategoryChip extends StatelessWidget {
271+ final String label;
272+ const _CategoryChip ({required this .label});
273+
274+ @override
275+ Widget build (BuildContext context) {
276+ return Chip (
277+ label: Text (label),
278+ backgroundColor: Colors .white,
279+ labelStyle: const TextStyle (
280+ color: Colors .black87,
281+ fontWeight: FontWeight .bold,
282+ ),
283+ side: const BorderSide (color: Colors .black12),
284+ shape: RoundedRectangleBorder (
285+ borderRadius: BorderRadius .circular (20 ),
286+ ),
287+ );
288+ }
289+ }
290+
291+ class _RecommendationCard extends StatelessWidget {
292+ final String image;
293+ const _RecommendationCard ({required this .image});
294+
295+ @override
296+ Widget build (BuildContext context) {
297+ return Container (
298+ width: 120 ,
299+ margin: const EdgeInsets .only (right: 16 ),
300+ decoration: BoxDecoration (
301+ borderRadius: BorderRadius .circular (16 ),
302+ image: DecorationImage (
303+ image: NetworkImage (image),
304+ fit: BoxFit .cover,
305+ ),
306+ boxShadow: const [
307+ BoxShadow (
308+ color: Colors .black12,
309+ blurRadius: 6 ,
310+ offset: Offset (0 , 2 ),
311+ ),
312+ ],
313+ ),
314+ );
315+ }
316+ }
0 commit comments