Movie Detail Screen (11) - Movie App | Industry Standard | Dev to Publish

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
Welcome back, Glad that you are here. We're building a Movie App with the best coding practices and tools. Till now in UI, we've made HomeScreen, Navigation Drawer and App Dialog. Now, let's work on Movie Detail Screen. This will be longer video than compared to others. You'll land on movie detail screen from 3 places. Tap on Movie Card from Home Screen, Search Screen and Favourite Movies Screen. On Movie Detail Screen, you'll see AppBar with back icon and favourite icon, which is a toggle icon. We will save movies in the local database in future videos so right now, I'll only place the icons in without giving action on tap. Then, we will have big poster of the movie, followed by Title and Description of the movie. To keep this video/article to a specific topic, I will create separate video for cast and video in movie details. Let's start coding. Whenever we design any screen, we should think of the data that it will display. As in case of Movie Detail Screen, we will show details of any selected movie. As you already know from our previous videos, domain layer consists of entities, abstract repository and usecases. Entity (Request & Response) From Data Source, we will get the movie detail by hitting an API. We will get so many unwanted fields from the data source, but entity must not have all those fields. Entity should only have the fields that required to show in UI or that are part of some further API calls. Let's create request and response entity keeping the domain only in mind. In the domain/entities folder, create a new file movie_params.dart: To fetch the movie details, we require a movie id. This class acts as a data holder for request parameters that are required to call the movie detail API. In the domain/entities folder, create a new file movie_detail_entity.dart: If you look at the data on Movie Detail Screen, you can see we need title, releaseDate, overview, voteAverage, and backdropPath. You will also need id and posterPath at some point in future, so let's add them too. Let's consider that id will be enough to compare two movie detail objects if required. When we are ready with the request and response entity, we can create an abstract method in the abstract repository. In the domain/repository folder, add a new method: Future<Either<AppError, MovieDetailEntity>> getMovieDetail(int id); Here, we create a method that takes in an id and returns us with either app error or a success object of MovieDetailEntity. Now, let's make a use case that invokes this method. If you have seen previous videos, we are using use cases only in the Blocs. Create a new use case in the domain/usecases folder: Since we have already created some usecases before, so copy any one of them and change the name to GetMovieDetail. Important thing here is that you define the response type and request type as MovieDetailEntity and MovieParams respectively. The call method works on these types, so change them as well with correct types. Here, you'll make call to getMovieDetail instead of getTrending. We're done with domain layer that easy and fast. Let's make the API call now via Data Layer. If you open the data folder, you'll see errors in repository folder. That's because we haven't implemented the method that we declared in abstract repository class. Before resolving the error, let's first create the model class. A model class is the response model, that holds the parsed JSON from the API. If you remember, we use https://javiercbk.github.io/json_to_dart/ for creating the model class from JSON. Head on to https://developers.themoviedb.org/3/ and select MOVIES. On the right side, you can see the details. Put the API_KEY and any movie id. Hit the SEND REQUEST button and copy the response. Paste this response in JSON2DART tool. Copy the generated code in a new file movie_detail_model.dart We have to make some modifications because sometimes based on JSON, the JSON2DART tool is unable to create proper sub classes. Make all the fields as final and make fromJSON method as factory method now. You can refer the [DataSources] video for the similar steps. While you're doing these code changes, you will also extend the model with entity and use the super constructor to fill in values in the entity instance. Now, you can add the implementation of repository method in movie_repository_impl.dart. Add async keyword in the method. You'll create the getMovieDetail() from the data source, which we will create in a moment. Next, as we have done for other methods to maintain a level of abstraction, use the model instead of entity. Now, we are at the final part of making the API call. In the movie_remote_data_source.dart, add the abstract method and the implementation: Add declaration in the abstract class MovieRemoteDataSource. Implement the method in MovieRemoteDataSourceImpl and add async. To fetch the movie details, you have to append movie/$id to the BASE_URL. Use fromJson() of MovieDetailModel to create model from JSON. Finally, return the MovieDetailModel. In this case, we can create a Bloc even before we have our UI. Because, Bloc only depends on making API calls, and we have use case already added. Let's create a bloc. Movie Detail Bloc Create bloc with name movie_detail: Add a new event in the movie_detail_event.dart: Declare MovieDetailLoadEvent class extending the MovieDetailEvent. Declare a final field movieId and override the props() method. Now, in movie_detail_state.dart add some states MovieDetailLoading for showing the loader, that we will cover in future videos. MovieDetailError for handling errors when returned from API or network. MovieDetailLoaded will be emitted when the movie detail api has responded with success MovieDetailEntity object. As you will store the movie details in the entity, declare the final field and add it in props as well. Now, handle the event and dispatch the state in the movie_detail_bloc.dart: As you need to make API call using use case, declare the final field of GetMovieDetail. Allow it to be passed it in constructor also. We will declare all dependencies things in get_it.dart in a while. Handle the event in mapEventToState. When you have event as load event, make the API call and store the response in Either type object. Use the fold operator to yield MovieDetailError or MovieDetailLoaded state with movie details object. We have declared a new use case and a bloc, let's put them in get_it.dart: Just like others, below all use cases, declare one more GetMovieDetail. And below all blocs, declare dependency for MovieDetailBloc. As I do it, I found that I have declared dependency for MovieTabbedBloc in wrong way. We have given new instances of each use case in the MovieTabbedBloc constructor. Instead, we should take from getItInstance directly. Update that This way, we are not having 2 instances of GetPopular, GetPlayingNow and GetComingSoon use case. As of now, we have 2 places from where we will navigate to the details screen. 1.On tap of Carousel Card 2. On tap of Tab Card From both of these places, you will get the movie id. This id you have to transmit to the details screen. This should be straight forward as you can directly pass id in the MovieDetailScreen constructor. At first, this seems easy and quick. But, we are talking about best practices, scalable and modular approach. Currently, you have only one parameter. What you would do when you have 10 parameters? Obviously, you will not like giving 10 fields in the constructor. For that, we will create a separate class that can have any number of arguments in future. We are entering a new journey, so create a folder in journeys and create MovieDetailArguments class in that folder Declare a field movieId, as that will be required to make the API call once we land on the screen. So, the main idea is when we tap on the movie card, we take the id to the detail screen and make an API call. Now, we have the arguments. So, let's make the screen. In the same folder, create a new file movie_detail_screen.dart: Create a Stateless widget and add the MovieDetailArguments as final field as the screen depends on this argument class. Make this field as required so that we make sure that screen doesn't get called without arguments by mistake. As this is a required field, let's also be assure that this will never be null. You might be thinking what happens when arguments are not null but he movieId in arguments is null. Well, assert statement can only take constants and movieId is not a constant. So, we cannot restrict this. But, in this case API will return with error and we will show user with appropriate error. Before moving to UI, let's link this screen to the carousel card and tab cards. Open movie_card_widget in home/movie_carousel folder and add the linking: In the onTap body, add the linking. Use the push method to push the MovieDetailScreen in the stack. You will also pass the MovieDetailArguments by using movieId. Do this same thing in home/movie_tabbed for movie_tab_card_widget.dart. For now we are using the push method of Navigator but as we grow in future videos with more screens, we will use pushNamed because that is more maintainable. Now, run the application and see what happens when we tap on movie cards. We will navigate to a white screen, that's because we have an empty container in the screen. Main Layout Finally, we are on UI. In the MovieDetailScreen add the Bloc and handle the success, error states: First, make the MovieDetailScreen as Stateful widget, as we will get the instance of MovieDetailBloc instance and dispose it here itself. Declare a private variable for the bloc. Get the instance in initState(). Dispose the bloc in dispose(). Dispatch the MovieLoadEvent in initState() , because as soon as we land on this screen we will get the movie details from the API based on movieId from MovieDetailArguments. In this build(), wrap the Container widget with BlocBuilder. Since we have the BlocBuilder, we must also provide the Bloc down the tree so that the child widgets can use that bloc. So, wrap BlocBuilder with BlocProvider and provide the value as _movieDetailBloc. This whole screen should be in a Scaffold. Comeback to BlocBuilder to handle the states. For now, just write if-else statements to handle loaded and error state. For rest of the states, we show SizedBox.shrink(). Run the app now and tap on any movie card. You'll navigate to movie detail screen with primary colour as the screen colour. This is because, we have added vulcan as scaffoldBackgroundColor in the ThemeData. Take the MovieDetailEntity out of the state to use it in the further UI. Use Column to layout elements in vertical order. We will put the top part of this screen in a separate widget - BigPoster. In the same folder, create a new widget file - big_poster.dart: Create a Stateless widget. This widget cannot run without movie details, so declare a final field of MovieDetailEntity type. Also, make sure that the movie details is a required field. Update the build() by adding Image: You'll start with using CachedNetworkImage with posterPath. As the backdropPath is in horizontal so we will use posterPath only. We will show name of the movie and release date at the bottom of this image, so we will require some overlay on top so that irrespective of any image, the title is visible. So, wrap this image with Container and give a foregroundDecoration. Use LinearGradient with 2 colours, first one being a little transparent with 0.3 opacity. We will use primary colour for this. By default, the direction of gradient is from left centre to right centre. So, give begin and end explicitly as we should have darker colour at the bottom side of the image. Now, we'll add the title, release date and average vote. Using Stack, we will use ListTile over the BigPoster. Put the Positioned widget below the BigPoster: We want to use full width, so give left and right as 0. Also, give bottom 0, because we want to position every text at the bottom of BigPoster. ListtTile is the best widget that can be used here as we need title, description and trailing widgets in the same positions. We will show title of the movie first. Give it headline5 widget. In subtitle, we will show releaseDate below the title. Give it greySubtitle text style, will create it in just a moment. Now, at the right most of the list tile, we need to show vote average. Give violetHeadline6 as text style. Open theme_text.dart and add 2 text styles in the extensions: By copying everything in subtitle1 and changing just the colour, we will create new text style. Similarly, copy all properties of headline6 and change to colour to violet. You might be wondering, why extensions are created at first place. Because text theme has text styles based on font sizes, not based on colours. So, when we have to use same text styles with different colours, it should be easier and consistent throughout the app. By creating an extension on TextTheme you can call these text styles by Theme.of(context).textTheme.greySubtitle1. This will help us when we add multiple themes and dark modes to the application. We need one more extension, this time on number though. The vote average that is returned by the API is in decimals and we want to convert it to %. Call convertToPercentageString on voteAverage and create num_extensions.dart in common/extensions folder: This method will multiple by 10 and then remove any further decimals. After this we will append % sign. In the BigPoster widget, add another element in Stack at the end. Use Positioned with some margin from left and right. Add MovieDetailAppBar that we will create now. In the journeys/movie_detail folder, create a new file movie_detail_app_bar.dart: Use Row to layout elements in horizontal. Use arrow_back_ios as the left most icon. Use favourite_border as the right most icon. To make these icons have unlimited space in between, use spaceBetween. From top, consider using the statusBarHeight from ScreenUtil because the row is touching the phone from top Use GestureDetector to navigate back to the home screen on click of the back arrow: Give onTap and pop out of the screen for the back arrow Now, you can navigate back and forth. Cast List and Videos Screen.
Info
Channel: Techie Blossom
Views: 2,816
Rating: undefined out of 5
Keywords: Flutter UI, Flutter SDK, MovieApp, Industry Standard, Plan, Develop, Publish, ForegroundDecoration, Movie Detail, ListTile, Bloc, Dependency Injection
Id: 9-opgVL5_VM
Channel Id: undefined
Length: 30min 2sec (1802 seconds)
Published: Fri Oct 30 2020
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.