diff --git a/.github/workflows/flutter_android.yml b/.github/workflows/flutter_android.yml index 375f49f..2ef83a9 100644 --- a/.github/workflows/flutter_android.yml +++ b/.github/workflows/flutter_android.yml @@ -50,8 +50,8 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: - tag_name: 0.0.3 - release_name: Version Release 0.0.3 + tag_name: 0.0.4 + release_name: Version Release 0.0.4 draft: false prerelease: false diff --git a/lib/dto/credit_person.dart b/lib/dto/credit_person.dart index 08e670b..7130f88 100644 --- a/lib/dto/credit_person.dart +++ b/lib/dto/credit_person.dart @@ -6,6 +6,7 @@ part 'credit_person.g.dart'; class CreditPerson { CreditPerson({ required this.id, + this.title, this.originalTitle, this.backdropPath, this.adult, @@ -13,11 +14,12 @@ class CreditPerson { this.overview, this.posterPath, this.originalLanguage, - // required this.releaseDate, + this.job, this.character, this.popularity, }); int id; + String? title; String? originalTitle; String? backdropPath; bool? adult; @@ -25,7 +27,7 @@ class CreditPerson { String? overview; String? posterPath; String? originalLanguage; - // String releaseDate; + String? job; String? character; double? popularity; diff --git a/lib/dto/credits_person.dart b/lib/dto/credits_person.dart new file mode 100644 index 0000000..a9cbdd4 --- /dev/null +++ b/lib/dto/credits_person.dart @@ -0,0 +1,20 @@ +import 'package:FilmFlu/dto/credit_person.dart'; +import 'package:json_annotation/json_annotation.dart'; + +part 'credits_person.g.dart'; + +@JsonSerializable(explicitToJson: false, fieldRename: FieldRename.snake) +class CreditsPerson { + CreditsPerson({ + required this.cast, + this.crew, + }); + + List cast; + List? crew; + + factory CreditsPerson.fromJson(Map json) => + _$CreditsPersonFromJson(json); + + Map toJson() => _$CreditsPersonToJson(this); +} diff --git a/lib/main.dart b/lib/main.dart index 12341dc..6ae647c 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,8 +1,4 @@ //Core Packages -import 'package:FilmFlu/ui/pages/main/main_screen.dart'; -import 'package:FilmFlu/ui/pages/personDetails/actor_details.dart'; -import 'package:FilmFlu/ui/pages/settings/settings_screen.dart'; -import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; import 'package:responsive_framework/responsive_framework.dart'; import 'package:flutter/material.dart'; @@ -11,10 +7,12 @@ import 'package:flutter_gen/gen_l10n/app_localizations.dart'; //My Packages import 'package:FilmFlu/ui/pages/login/login_page.dart'; -import 'package:FilmFlu/ui/pages/splash/splash_screen.dart'; import 'package:FilmFlu/dto/base_arguments.dart'; import 'package:FilmFlu/ui/pages/movieDetails/movie_details.dart'; import 'package:FilmFlu/ui/theme/colors.dart'; +import 'package:FilmFlu/ui/pages/main/main_screen.dart'; +import 'package:FilmFlu/ui/pages/personDetails/actor_details.dart'; +import 'package:FilmFlu/ui/pages/settings/settings_screen.dart'; import 'package:FilmFlu/ui/util/utilScroll.dart'; void main() { @@ -107,7 +105,7 @@ class FilmFlu extends StatelessWidget { screen = SettingsPage(); break; default: - screen = !kIsWeb ? SplashScreen() : MainPage(); + screen = MainPage(); break; } return MaterialPageRoute(builder: (context) { diff --git a/lib/network/client_api.dart b/lib/network/client_api.dart index 7def1ad..362f2bb 100644 --- a/lib/network/client_api.dart +++ b/lib/network/client_api.dart @@ -1,6 +1,7 @@ //Core Packages import 'dart:convert'; import 'package:FilmFlu/dto/credit_person.dart'; +import 'package:FilmFlu/dto/credits_person.dart'; import 'package:flutter/foundation.dart'; import 'package:http/http.dart'; @@ -23,6 +24,12 @@ class Api { return parsed.map((json) => Movie.fromJson(json)).toList(); } + CreditsPerson parseCreditsPerson(String responseBody) { + final parsed = jsonDecode(responseBody); + CreditsPerson credits = CreditsPerson.fromJson(parsed); + return credits; + } + Credits parseCredits(String responseBody) { final parsed = jsonDecode(responseBody); Credits credits = Credits.fromJson(parsed); @@ -35,8 +42,8 @@ class Api { return person; } - List parsePersonCredits(String responseBody) { - final parsed = jsonDecode(responseBody)["cast"]; + List parsePersonCredits(String responseBody) { + final parsed = jsonDecode(responseBody); return parsed .map((json) => CreditPerson.fromJson(json)) .toList(); @@ -90,11 +97,11 @@ class Api { return compute(parsePerson, response.body); } - Future> fetchPersonCredits(int personId) async { + Future fetchPersonCredits(int personId) async { final response = await Client().get( Uri.parse( '$baseURL/person/${personId}/combined_credits?language=es-ES'), headers: baseHeaders); - return compute(parsePersonCredits, response.body); + return compute(parseCreditsPerson, response.body); } } diff --git a/lib/ui/components/film_actor_cast_item.dart b/lib/ui/components/film_actor_cast_item.dart index 482a83e..38d6388 100644 --- a/lib/ui/components/film_actor_cast_item.dart +++ b/lib/ui/components/film_actor_cast_item.dart @@ -33,7 +33,7 @@ class _FilmActorItemState extends State { child: ClipRRect( borderRadius: BorderRadius.circular(32.0), child: Image.network('$personImgBaseUrl${actor.profilePath}', - height: 220, + height: 160, width: 150, fit: BoxFit.cover, loadingBuilder: (BuildContext context, Widget child, ImageChunkEvent? loadingProgress) { @@ -50,14 +50,14 @@ class _FilmActorItemState extends State { if (actor.gender == 2) { return SvgPicture.asset( "assets/icons/actor_icon.svg", - height: 220, + height: 160, fit: BoxFit.cover, width: 150, ); } else { return SvgPicture.asset( "assets/icons/actress_icon.svg", - height: 220, + height: 160, fit: BoxFit.cover, width: 150, ); diff --git a/lib/ui/components/film_worker_cast_item.dart b/lib/ui/components/film_worker_cast_item.dart index 55666ab..df5289a 100644 --- a/lib/ui/components/film_worker_cast_item.dart +++ b/lib/ui/components/film_worker_cast_item.dart @@ -21,7 +21,6 @@ class _FilmWorkerItemState extends State { int index = widget.index; List crew = widget.crew; FilmWorker filmWorker = crew[index]; - debugPrint(filmWorker.job); return GridTile( child: Column( children: [ @@ -33,7 +32,7 @@ class _FilmWorkerItemState extends State { child: ClipRRect( borderRadius: BorderRadius.circular(32.0), child: Image.network('$personImgBaseUrl${filmWorker.profilePath}', - height: 220, + height: 160, width: 150, fit: BoxFit.cover, loadingBuilder: (BuildContext context, Widget child, ImageChunkEvent? loadingProgress) { @@ -50,14 +49,14 @@ class _FilmWorkerItemState extends State { if (filmWorker.gender == 2) { return SvgPicture.asset( "assets/icons/actor_icon.svg", - height: 220, + height: 160, fit: BoxFit.cover, width: 150, ); } else { return SvgPicture.asset( "assets/icons/actress_icon.svg", - height: 220, + height: 160, fit: BoxFit.cover, width: 150, ); diff --git a/lib/ui/components/movie_cast.dart b/lib/ui/components/movie_cast.dart index 0d8216c..3bfc3d4 100644 --- a/lib/ui/components/movie_cast.dart +++ b/lib/ui/components/movie_cast.dart @@ -41,9 +41,9 @@ class _FilmCastState extends State { shrinkWrap: true, gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent( maxCrossAxisExtent: 150, - mainAxisSpacing: 20, + mainAxisSpacing: 0, crossAxisSpacing: 40, - mainAxisExtent: 340, + mainAxisExtent: 250, childAspectRatio: MediaQuery.of(context).size.aspectRatio, ), itemCount: widget.isCast ? cast?.length : crew?.length, diff --git a/lib/ui/components/scaffold_page.dart b/lib/ui/components/scaffold_page.dart index aeafcdc..3895b19 100644 --- a/lib/ui/components/scaffold_page.dart +++ b/lib/ui/components/scaffold_page.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:package_info_plus/package_info_plus.dart'; import 'package:url_launcher/url_launcher.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; @@ -51,7 +52,23 @@ class _ScaffoldPageState extends State { backgroundColor: Theme.of(context).colorScheme.onBackground, appBar: widget.isLightsOn == true ? AppBar( - automaticallyImplyLeading: false, + leading: Padding( + padding: const EdgeInsets.all(12.0), + child: InkWell( + child: Image.asset( + 'assets/images/transparent_logo.png', + height: 20, + width: 20, + fit: BoxFit.fitWidth, + ), + onTap: () { + if (Navigator.canPop(context)) + Navigator.pop(context); + else + SystemChannels.platform + .invokeMethod('SystemNavigator.pop'); + }), + ), toolbarHeight: 100, flexibleSpace: Padding( padding: const EdgeInsets.only(left: 48, right: 48), @@ -99,12 +116,6 @@ class _ScaffoldPageState extends State { : Text(widget.routeName), ], )))), - title: InkWell( - child: Image.asset('assets/images/transparent_logo.png', - height: 50), - onTap: () { - Navigator.pushNamed(context, "/"); - }), elevation: 1, scrolledUnderElevation: 20, backgroundColor: Theme.of(context).colorScheme.background, diff --git a/lib/ui/pages/movieDetails/movie_details.dart b/lib/ui/pages/movieDetails/movie_details.dart index 75e6509..8c1b7f9 100644 --- a/lib/ui/pages/movieDetails/movie_details.dart +++ b/lib/ui/pages/movieDetails/movie_details.dart @@ -1,13 +1,13 @@ //Core Packages; import 'package:FilmFlu/ui/theme/colors.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:youtube_player_iframe/youtube_player_iframe.dart'; import 'package:auto_size_text/auto_size_text.dart'; //My Packages import 'package:FilmFlu/dto/movie.dart'; -import 'package:FilmFlu/ui/pages/splash/splash_screen.dart'; import 'package:FilmFlu/network/client_api.dart'; import 'package:FilmFlu/ui/components/scaffold_page.dart'; import 'package:FilmFlu/ui/components/movie_cast.dart'; @@ -59,7 +59,7 @@ class _MovieDetailsPageState extends State { isSearchVisible: true, isLightsOn: !isTrailerSelected, floatingActionButton: Padding( - padding: const EdgeInsets.all(16), + padding: const EdgeInsets.only(top: 24), child: isTrailerSelected ? FloatingActionButton( mini: true, @@ -71,6 +71,12 @@ class _MovieDetailsPageState extends State { _trailerController.stopVideo(); _trailerController.close(); isTrailerSelected = false; + SystemChrome.setPreferredOrientations([ + DeviceOrientation.portraitDown, + DeviceOrientation.portraitUp, + ]); + SystemChrome.setEnabledSystemUIMode( + SystemUiMode.edgeToEdge); }); }, ) @@ -286,7 +292,10 @@ class _MovieDetailsPageState extends State { ], ); } else { - return SplashScreen(); + return Container( + width: MediaQuery.of(context).size.width, + height: MediaQuery.of(context).size.height, + child: Center(child: CircularProgressIndicator())); } }, ), @@ -295,6 +304,10 @@ class _MovieDetailsPageState extends State { : FutureBuilder>( future: fetchMovieTrailers("es-ES"), builder: (context, snapshot) { + SystemChrome.setPreferredOrientations([ + DeviceOrientation.landscapeRight, + DeviceOrientation.landscapeLeft, + ]); return YoutubePlayerScaffold( controller: _trailerController, builder: (context, player) { diff --git a/lib/ui/pages/personDetails/actor_details.dart b/lib/ui/pages/personDetails/actor_details.dart index 98f179b..9a6ec92 100644 --- a/lib/ui/pages/personDetails/actor_details.dart +++ b/lib/ui/pages/personDetails/actor_details.dart @@ -1,8 +1,11 @@ //Core Packages +import 'package:FilmFlu/dto/credits.dart'; +import 'package:FilmFlu/dto/credits_person.dart'; +import 'package:FilmFlu/ui/pages/movieDetails/movie_details.dart'; +import 'package:FilmFlu/ui/theme/colors.dart'; import 'package:auto_size_text/auto_size_text.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_svg/flutter_svg.dart'; //My Packages import 'package:FilmFlu/constants.dart'; @@ -22,6 +25,8 @@ class ActorDetailsPage extends StatefulWidget { } class _ActorDetailsPage extends State { + bool isActorWorkSelected = true; + @override Widget build(BuildContext context) { return ScaffoldPage( @@ -45,8 +50,6 @@ class _ActorDetailsPage extends State { child: Center(child: CircularProgressIndicator())); } else if (person != null) { return Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( mainAxisAlignment: MainAxisAlignment.start, @@ -99,6 +102,30 @@ class _ActorDetailsPage extends State { ], ), ), + person.deathday != null + ? Padding( + padding: + const EdgeInsets.only(top: 16.0), + child: Row( + children: [ + Icon( + Icons.abc, + color: Colors.white, + ), + Padding( + padding: const EdgeInsets.only( + left: 8.0), + child: Text( + "${parseDate(person.deathday!)}", + textAlign: TextAlign.start, + style: TextStyle( + color: Colors.white)), + ), + ], + ), + ) + : Container(), + SizedBox(height: 20), Row( children: [ Icon( @@ -107,17 +134,17 @@ class _ActorDetailsPage extends State { ), Padding( padding: const EdgeInsets.only(left: 8.0), - child: - AutoSizeText("${person.placeOfBirth}", - maxFontSize: 16, - minFontSize: 13, - overflow: TextOverflow.ellipsis, - textAlign: TextAlign.start, - maxLines: 2, - style: TextStyle( - color: Colors.white, - fontSize: 13, - )), + child: SizedBox( + width: 180, + child: AutoSizeText( + "${person.placeOfBirth.trim()}", + maxFontSize: 20, + minFontSize: 15, + overflow: TextOverflow.ellipsis, + maxLines: 3, + style: + TextStyle(color: Colors.white)), + ), ), ], ), @@ -146,96 +173,273 @@ class _ActorDetailsPage extends State { "Papeles que ha realizado", textAlign: TextAlign.center, style: TextStyle( - fontSize: 24, color: Colors.white), + fontSize: 20, color: Colors.white), + ), + ), + ), + Center( + child: Padding( + padding: const EdgeInsets.only(bottom: 32.0), + child: SegmentedButton( + selectedIcon: null, + emptySelectionAllowed: false, + showSelectedIcon: false, + selected: {isActorWorkSelected}, + style: ButtonStyle( + backgroundColor: MaterialStateProperty + .resolveWith( + (Set states) { + if (states + .contains(MaterialState.selected)) { + return primaryColor; + } + return Colors.white24; + }, + ), + ), + segments: [ + ButtonSegment( + label: Text( + AppLocalizations.of(context)! + .character_cast, + style: TextStyle( + color: Colors.white, + fontSize: 20, + ), + ), + value: true, + ), + ButtonSegment( + label: Text( + AppLocalizations.of(context)! + .production_cast, + style: TextStyle( + color: Colors.white, + fontSize: 20, + ), + ), + value: false, + ), + ], + onSelectionChanged: (Set newSelection) { + setState(() { + isActorWorkSelected = newSelection.first; + }); + }, ), ), ), - FutureBuilder>( + FutureBuilder( future: Api().fetchPersonCredits(widget.actorId), builder: (context, snapshot) { if (snapshot.connectionState != ConnectionState.waiting && snapshot.hasData) { - List creditsList = - snapshot.requireData; - return GridView.builder( - controller: TrackingScrollController(), - itemCount: creditsList.length, - shrinkWrap: true, - gridDelegate: - SliverGridDelegateWithMaxCrossAxisExtent( - maxCrossAxisExtent: 150, - mainAxisSpacing: 20, - crossAxisSpacing: 40, - mainAxisExtent: 340, - childAspectRatio: MediaQuery.of(context) - .size - .aspectRatio, - ), - itemBuilder: (context, index) { - CreditPerson filmPerson = - creditsList[index]; - return GridTile( - child: Column( - children: [ - ClipRRect( - borderRadius: - BorderRadius.circular(32.0), - child: Image.network( - filmPerson.backdropPath == null - ? "$personImgBaseUrl${filmPerson.posterPath}" - : "$personImgBaseUrl${filmPerson.backdropPath}", - height: 220, - width: 150, - fit: BoxFit.cover, - loadingBuilder: - (BuildContext context, - Widget child, - ImageChunkEvent? - loadingProgress) { - if (loadingProgress == null) - return child; - return Center( - child: - CircularProgressIndicator( - value: loadingProgress - .expectedTotalBytes != - null - ? loadingProgress - .cumulativeBytesLoaded / - loadingProgress - .expectedTotalBytes! - : null, + List creditsListAsActor = + snapshot.data!.cast; + List? creditsListAsProduction = + snapshot.data?.crew; + return isActorWorkSelected + ? GridView.builder( + controller: + TrackingScrollController(), + itemCount: creditsListAsActor.length, + shrinkWrap: true, + gridDelegate: + SliverGridDelegateWithMaxCrossAxisExtent( + maxCrossAxisExtent: 150, + mainAxisSpacing: 20, + crossAxisSpacing: 40, + mainAxisExtent: 340, + childAspectRatio: + MediaQuery.of(context) + .size + .aspectRatio, + ), + itemBuilder: (context, index) { + CreditPerson filmPerson = + creditsListAsActor[index]; + String? movieTitle = filmPerson + .title != + null + ? "${filmPerson.character} ${AppLocalizations.of(context)?.in_preposition} ${filmPerson.title}" + : "${filmPerson.character}"; + return InkWell( + onTap: () { + Navigator.of(context).push( + MaterialPageRoute( + builder: (_) => + MovieDetailsPage( + isTrailerSelected: + false, + movieId: + filmPerson + .id))); + }, + child: GridTile( + child: Column( + children: [ + ClipRRect( + borderRadius: + BorderRadius.circular( + 32.0), + child: Image.network( + filmPerson.backdropPath != + null + ? "$personImgBaseUrl${filmPerson.backdropPath}" + : "$personImgBaseUrl${person.profilePath}", + height: 220, + width: 150, + fit: BoxFit.cover, + loadingBuilder: + (BuildContext + context, + Widget child, + ImageChunkEvent? + loadingProgress) { + if (loadingProgress == + null) + return child; + return Center( + child: + CircularProgressIndicator( + value: loadingProgress + .expectedTotalBytes != + null + ? loadingProgress + .cumulativeBytesLoaded / + loadingProgress + .expectedTotalBytes! + : null, + ), + ); + }, + ), ), - ); - }, - // errorBuilder: - // (context, url, error) { - // return SvgPicture.asset( - // "assets/icons/placeholder_image.svg", - // height: 220, - // fit: BoxFit.cover, - // width: 150, - // ); - // }, + Padding( + padding: + const EdgeInsets.only( + top: 16.0), + child: AutoSizeText( + "${AppLocalizations.of(context)?.actor_job} $movieTitle", + textAlign: + TextAlign.center, + maxLines: 3, + minFontSize: 14, + overflow: TextOverflow + .ellipsis, + style: TextStyle( + fontSize: 16, + color: + Colors.white), + ), + ), + ], + ), ), - ), - Padding( - padding: const EdgeInsets.only( - top: 16.0), - child: Text( - "${AppLocalizations.of(context)?.actor_job} ${filmPerson.character}", - textAlign: TextAlign.center, - style: TextStyle( - fontSize: 18, - color: Colors.white), + ); + }, + ) + : GridView.builder( + controller: + TrackingScrollController(), + itemCount: + creditsListAsProduction?.length, + shrinkWrap: true, + gridDelegate: + SliverGridDelegateWithMaxCrossAxisExtent( + maxCrossAxisExtent: 150, + mainAxisSpacing: 20, + crossAxisSpacing: 40, + mainAxisExtent: 340, + childAspectRatio: + MediaQuery.of(context) + .size + .aspectRatio, + ), + itemBuilder: (context, index) { + CreditPerson filmPerson = + creditsListAsProduction![index]; + String? movieTitle = filmPerson + .title != + null + ? "${filmPerson.job} ${AppLocalizations.of(context)?.in_preposition} ${filmPerson.title}" + : "${filmPerson.job}"; + return InkWell( + onTap: () { + Navigator.of(context).push( + MaterialPageRoute( + builder: (_) => + MovieDetailsPage( + isTrailerSelected: + false, + movieId: + filmPerson + .id))); + }, + child: GridTile( + child: Column( + children: [ + ClipRRect( + borderRadius: + BorderRadius.circular( + 32.0), + child: Image.network( + filmPerson.backdropPath != + null + ? "$personImgBaseUrl${filmPerson.backdropPath}" + : "$personImgBaseUrl${person.profilePath}", + height: 220, + width: 150, + fit: BoxFit.cover, + loadingBuilder: + (BuildContext + context, + Widget child, + ImageChunkEvent? + loadingProgress) { + if (loadingProgress == + null) + return child; + return Center( + child: + CircularProgressIndicator( + value: loadingProgress + .expectedTotalBytes != + null + ? loadingProgress + .cumulativeBytesLoaded / + loadingProgress + .expectedTotalBytes! + : null, + ), + ); + }, + ), + ), + Padding( + padding: + const EdgeInsets.only( + top: 16.0), + child: AutoSizeText( + "${AppLocalizations.of(context)?.production_job} $movieTitle", + textAlign: + TextAlign.center, + maxLines: 3, + minFontSize: 14, + overflow: TextOverflow + .ellipsis, + style: TextStyle( + fontSize: 16, + color: + Colors.white), + ), + ), + ], + ), ), - ), - ], - ), - ); - }, - ); + ); + }); } else { return Container( width: MediaQuery.of(context).size.width, @@ -245,7 +449,7 @@ class _ActorDetailsPage extends State { child: CircularProgressIndicator())); } }, - ), + ) ], ), ), diff --git a/pubspec.yaml b/pubspec.yaml index fa22b36..e7a9b37 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,7 +1,7 @@ name: FilmFlu description: A project of movies and something more publish_to: 'none' # Remove this line if you wish to publish to pub.dev -version: 0.0.3+3 +version: 0.0.4+4 environment: sdk: '>=3.0.5 <4.0.0'