Skip to content

Commit 469669d

Browse files
committed
- Implemented feature to display all movies for an artist
1 parent 31c56fc commit 469669d

File tree

10 files changed

+152
-73
lines changed

10 files changed

+152
-73
lines changed

ios/Runner/Info.plist

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
<key>CFBundleDevelopmentRegion</key>
66
<string>$(DEVELOPMENT_LANGUAGE)</string>
77
<key>CFBundleDisplayName</key>
8-
<string>Flutter Movie Clean Architecture</string>
8+
<string>Flutter Movie</string>
99
<key>CFBundleExecutable</key>
1010
<string>$(EXECUTABLE_NAME)</string>
1111
<key>CFBundleIdentifier</key>

lib/core/network/dio_provider.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,14 @@ final dioProvider = Provider<Dio>((ref) {
1010
'api_key': API_KEY,
1111
},
1212
));
13-
dio.interceptors.add(PrettyDioLogger(
13+
/*dio.interceptors.add(PrettyDioLogger(
1414
requestHeader: true,
1515
requestBody: true,
1616
responseBody: true,
1717
responseHeader: false,
1818
error: true,
1919
compact: true,
2020
maxWidth: 90,
21-
));
21+
));*/
2222
return dio;
2323
});

lib/features/movie/data/datasources/movie_remote_data_source.dart

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,4 +76,8 @@ class MovieRemoteDataSource {
7676
final response = await dio.get('person/$artistId');
7777
return ArtistDetailModel.fromJson(response.data as Map<String, dynamic>);
7878
}
79+
Future<CreditModel> getArtistAllMovies(int artistId) async {
80+
final response = await dio.get('person/$artistId/combined_credits');
81+
return CreditModel.fromJson(response.data as Map<String, dynamic>);
82+
}
7983
}

lib/features/movie/data/models/credit_model.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ class Cast with _$Cast {
2525
@JsonKey(name: 'original_name') String? originalName,
2626
double? popularity,
2727
@JsonKey(name: 'profile_path') String? profilePath,
28+
@JsonKey(name: 'poster_path') String? posterPath,
2829
@JsonKey(name: 'cast_id') int? castId,
2930
String? character,
3031
@JsonKey(name: 'credit_id') String? creditId,

lib/features/movie/data/repositories/movie_repository_impl.dart

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,4 +132,13 @@ class MovieRepositoryImpl implements MovieRepository {
132132
birthday: model.birthday ?? '',
133133
);
134134
}
135+
136+
Future<CreditModel> getArtistAllMovies(int artistId) async {
137+
final model = await remoteDataSource.getArtistAllMovies(artistId);
138+
return CreditModel(
139+
id: model.id,
140+
cast: model.cast,
141+
crew: model.crew,
142+
);
143+
}
135144
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import 'package:flutter_movie_clean_architecture/features/movie/data/models/movie_model.dart';
2+
3+
class Artistallmovies {
4+
final int id;
5+
final List<MovieModel> cast;
6+
7+
Artistallmovies({
8+
required this.id,
9+
required this.cast,
10+
});
11+
}

lib/features/movie/domain/repositories/entities/movie_repository.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import 'package:flutter_movie_clean_architecture/features/movie/domain/entities/
33
import 'package:flutter_movie_clean_architecture/features/movie/domain/entities/movie.dart';
44
import 'package:flutter_movie_clean_architecture/features/movie/domain/entities/movie_detail.dart';
55

6-
abstract class MovieRepository {
6+
abstract class MovieRepository {
77
Future<List<Movie>> getNowPlaying(int page);
88
Future<List<Movie>> getPopular(int page);
99
Future<List<Movie>> getTopRated(int page);
@@ -13,4 +13,5 @@ abstract class MovieRepository {
1313
Future<List<Movie>> getRecommendedMovie(int movieId);
1414
Future<CreditModel> getMovieCredits(int movieId);
1515
Future<Artistdetail> getArtistDetail(int artistId);
16+
Future<CreditModel> getArtistAllMovies(int artistId);
1617
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import 'package:flutter_movie_clean_architecture/features/movie/data/models/credit_model.dart';
2+
import 'package:flutter_movie_clean_architecture/features/movie/domain/repositories/entities/movie_repository.dart';
3+
4+
class GetAllArtistMovies {
5+
final MovieRepository repository;
6+
7+
GetAllArtistMovies(this.repository);
8+
9+
Future<CreditModel> call(int artistId) => repository.getArtistAllMovies(artistId);
10+
}

lib/features/movie/presentation/pages/artist_detail_page.dart

Lines changed: 102 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -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
}

lib/features/movie/presentation/providers/movie_provider.dart

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import 'package:flutter_movie_clean_architecture/features/movie/data/repositorie
55
import 'package:flutter_movie_clean_architecture/features/movie/domain/entities/ArtistDetail.dart';
66
import 'package:flutter_movie_clean_architecture/features/movie/domain/entities/movie.dart';
77
import 'package:flutter_movie_clean_architecture/features/movie/domain/entities/movie_detail.dart';
8+
import 'package:flutter_movie_clean_architecture/features/movie/domain/repositories/usecases/get_all_artist_movies.dart';
89
import 'package:flutter_movie_clean_architecture/features/movie/domain/repositories/usecases/get_artist_detail.dart';
910
import 'package:flutter_movie_clean_architecture/features/movie/domain/repositories/usecases/get_movie_credits.dart'
1011
show GetMovieCredits;
@@ -105,3 +106,12 @@ final artistDetailProvider =
105106
FutureProvider.family<Artistdetail, int>((ref, movieId) async {
106107
return ref.watch(getArtistDetailProvider).call(movieId);
107108
});
109+
110+
final getArtistAllMoviesProvider = Provider(
111+
(ref) => GetAllArtistMovies(ref.watch(movieRepositoryProvider)),
112+
);
113+
114+
final artistDetailAllMoviesProvider =
115+
FutureProvider.family<CreditModel, int>((ref, artistId) async {
116+
return ref.watch(getArtistAllMoviesProvider).call(artistId);
117+
});

0 commit comments

Comments
 (0)