Open In App

Android Jetpack Compose - Create a Movie App

Last Updated : 18 May, 2025
Summarize
Comments
Improve
Suggest changes
Share
Like Article
Like
Report

In the modern digital era, the demand for movie apps is soaring. These applications provide users with an immersive experience, allowing them to discover, explore, and enjoy a wide range of movies. Jetpack Compose, a declarative UI toolkit for Android, has gained immense popularity due to its simplicity, flexibility, and powerful features.

In this article, we will explore how to create a movie app using Jetpack Compose. A sample video is given at the end to get an idea about what we are going to do in this article.

Step by Step Implementation

Step 1: Create a New Project in Android Studio

To create a new project in the Android Studio, please refer to How to Create a new Project in Android Studio with Jetpack Compose.

Step 2: Add dependencies for navigation and image loading

Navigate to Gradle Scripts > build.gradle.kts (Module :app) and the following dependencies under dependencies{} tag.

dependencies {
...
implementation ("androidx.navigation:navigation-compose:2.7.7")
implementation("io.coil-kt:coil-compose:2.4.0")
}

Step 3: Setup Navigation

Create a Kotlin file with the name Navigation.kt to setup navigation using the NavHost composable. Create an enum class in kotlin with the name MovieScreens.kt to define screens.

Navigation.kt
package com.geeksforgeeks.demo

import androidx.compose.runtime.Composable
import androidx.navigation.NavType
import androidx.navigation.compose.*
import androidx.navigation.navArgument

@Composable
fun MovieNavigation() {
    val navController = rememberNavController()
    NavHost(navController = navController, startDestination = MovieScreens.HomeScreen.name) {
        composable(MovieScreens.HomeScreen.name) {
            MovieListScreen(navController = navController)
        }
        composable(
            MovieScreens.DetailsScreen.name + "/{movie}",
            arguments = listOf(navArgument("movie") { type = NavType.StringType })
        ) { backStackEntry ->
            DetailsScreen(
                navController = navController,
                movieId = backStackEntry.arguments?.getString("movie")
            )
        }
    }
}
MovieScreens.kt
package com.geeksforgeeks.demo

import androidx.compose.runtime.Composable
import androidx.navigation.NavType
import androidx.navigation.compose.*
import androidx.navigation.navArgument

@Composable
fun MovieNavigation() {
    val navController = rememberNavController()

    // Define navigation graph
    NavHost(
        navController = navController,
        // Initial screen
        startDestination = MovieScreens.HomeScreen.name
    ) {
        // Home screen route
        composable(MovieScreens.HomeScreen.name) {
            MovieListScreen(navController = navController)
        }

        // Details screen route with movie ID as argument
        composable(
            route = MovieScreens.DetailsScreen.name + "/{movie}",
            arguments = listOf(navArgument("movie") { type = NavType.StringType })
        ) { backStackEntry ->
            DetailsScreen(
                navController = navController,
                // Get movie ID from route
                movieId = backStackEntry.arguments?.getString("movie")
            )
        }
    }
}


Step 4: Create a data class to store Movie data

Create a kotlin data class with the name Movie.kt and create a function inside the file with the name getMovies() where we are going to add some movie data. Add the following code inside the file.

Movie.kt:

Movie.kt
package com.geeksforgeeks.demo

// Data model representing a Movie
data class Movie(
    val id: String,
    val title: String,
    val year: String,
    val genre: String,
    val director: String,
    val actors: String,
    val plot: String,
    // URL to the main poster image
    val poster: String,
    // List of additional images
    val images: List<String>,
    val rating: String
)

// Returns a hardcoded list of Movie objects
fun getMovies() : List<Movie> {
    return listOf(
        Movie(
            id = "101",
            title = "Avatar",
            images  = listOf(
                "https://images-na.ssl-images-amazon.com/images/M/MV5BMjEyOTYyMzUxNl5BMl5BanBnXkFtZTcwNTg0MTUzNA@@._V1_SX1500_CR0,0,1500,999_AL_.jpg",
                "https://images-na.ssl-images-amazon.com/images/M/MV5BNzM2MDk3MTcyMV5BMl5BanBnXkFtZTcwNjg0MTUzNA@@._V1_SX1777_CR0,0,1777,999_AL_.jpg",
                "https://images-na.ssl-images-amazon.com/images/M/MV5BMTY2ODQ3NjMyMl5BMl5BanBnXkFtZTcwODg0MTUzNA@@._V1_SX1777_CR0,0,1777,999_AL_.jpg",
                "https://images-na.ssl-images-amazon.com/images/M/MV5BMTMxOTEwNDcxN15BMl5BanBnXkFtZTcwOTg0MTUzNA@@._V1_SX1777_CR0,0,1777,999_AL_.jpg",
                "https://images-na.ssl-images-amazon.com/images/M/MV5BMTYxMDg1Nzk1MV5BMl5BanBnXkFtZTcwMDk0MTUzNA@@._V1_SX1500_CR0,0,1500,999_AL_.jpg"
            ),
            year = "2009",
            genre = "Action",
            director = "James Cameron",
            actors = "Sam Worthington, Zoe Saldana, Sigourney Weaver, Stephen Lang",
            plot = "A paraplegic marine dispatched to the moon Pandora on a unique mission becomes torn between following his orders and protecting the world he feels is his home.",
            poster = "https://m.media-amazon.com/images/M/MV5BMDEzMmQwZjctZWU2My00MWNlLWE0NjItMDJlYTRlNGJiZjcyXkEyXkFqcGc@._V1_.jpg",
            rating = "7.9"
        ) ,
        Movie(
            id = "102",
            title = "I Am Legend",
            images  = listOf(
                "https://images-na.ssl-images-amazon.com/images/M/MV5BMTI0NTI4NjE3NV5BMl5BanBnXkFtZTYwMDA0Nzc4._V1_.jpg",
                "https://images-na.ssl-images-amazon.com/images/M/MV5BMTIwMDg2MDU4M15BMl5BanBnXkFtZTYwMTA0Nzc4._V1_.jpg",
                "https://images-na.ssl-images-amazon.com/images/M/MV5BMTc5MDM1OTU5OV5BMl5BanBnXkFtZTYwMjA0Nzc4._V1_.jpg",
                "https://images-na.ssl-images-amazon.com/images/M/MV5BMTA0MTI2NjMzMzFeQTJeQWpwZ15BbWU2MDMwNDc3OA@@._V1_.jpg"
            ),
            year = "2007",
            genre = "Drama, Horror, Sci-Fi",
            director = "Francis Lawrence",
            actors = "Will Smith, Alice Braga, Charlie Tahan, Salli Richardson-Whitfield",
            plot = "Years after a plague kills most of humanity and transforms the rest into monsters, the sole survivor in New York City struggles valiantly to find a cure.",
            poster = "https://m.media-amazon.com/images/M/MV5BMGE1OWZkZmItNmVhMC00YzAxLTgxOTctNjg3NWExM2RmOWJkXkEyXkFqcGc@._V1_.jpg",
            rating = "9.8"
        ) ,
        Movie (
            id = "103",
            title ="The Avengers",
            year =  "2012",
            director =  "Joss Whedon",
            images = listOf(
                "https://images-na.ssl-images-amazon.com/images/M/MV5BMTA0NjY0NzE4OTReQTJeQWpwZ15BbWU3MDczODg2Nzc@._V1_SX1777_CR0,0,1777,999_AL_.jpg",
                "https://images-na.ssl-images-amazon.com/images/M/MV5BMjE1MzEzMjcyM15BMl5BanBnXkFtZTcwNDM4ODY3Nw@@._V1_SX1777_CR0,0,1777,999_AL_.jpg",
                "https://images-na.ssl-images-amazon.com/images/M/MV5BMjMwMzM2MTg1M15BMl5BanBnXkFtZTcwNjM4ODY3Nw@@._V1_SX1777_CR0,0,1777,999_AL_.jpg",
                "https://images-na.ssl-images-amazon.com/images/M/MV5BMTQ4NzM2Mjc5MV5BMl5BanBnXkFtZTcwMTkwOTY3Nw@@._V1_SX1777_CR0,0,1777,999_AL_.jpg",
                "https://images-na.ssl-images-amazon.com/images/M/MV5BMTc3MzQ3NjA5N15BMl5BanBnXkFtZTcwMzY5OTY3Nw@@._V1_SX1777_CR0,0,1777,999_AL_.jpg"
            ) ,
            genre = "Action, Sci-Fi, Thriller",
            actors = "Robert Downey Jr., Chris Evans, Mark Ruffalo, Chris Hemsworth",
            plot = "Earth's mightiest heroes must come together and learn to fight as a team if they are to stop the mischievous Loki and his alien army from enslaving humanity.",
            poster = "https://m.media-amazon.com/images/M/MV5BNGE0YTVjNzUtNzJjOS00NGNlLTgxMzctZTY4YTE1Y2Y1ZTU4XkEyXkFqcGc@._V1_FMjpg_UX1000_.jpg",
            rating = "8.1",
        ),
        Movie(
            id = "104",
            title ="The Wolf of Wall Street",
            year =  "2013",
            director =  "Martin Scorsese",
            images = listOf(
                "https://images-na.ssl-images-amazon.com/images/M/MV5BNDIwMDIxNzk3Ml5BMl5BanBnXkFtZTgwMTg0MzQ4MDE@._V1_SX1500_CR0,0,1500,999_AL_.jpg",
                "https://images-na.ssl-images-amazon.com/images/M/MV5BMTc0NzAxODAyMl5BMl5BanBnXkFtZTgwMDg0MzQ4MDE@._V1_SX1500_CR0,0,1500,999_AL_.jpg",
                "https://images-na.ssl-images-amazon.com/images/M/MV5BMTExMDk1MDE4NzVeQTJeQWpwZ15BbWU4MDM4NDM0ODAx._V1_SX1500_CR0,0,1500,999_AL_.jpg",
                "https://images-na.ssl-images-amazon.com/images/M/MV5BMTg3MTY4NDk4Nl5BMl5BanBnXkFtZTgwNjc0MzQ4MDE@._V1_SX1500_CR0,0,1500,999_AL_.jpg",
                "https://images-na.ssl-images-amazon.com/images/M/MV5BMTgzMTg4MDI0Ml5BMl5BanBnXkFtZTgwOTY0MzQ4MDE@._V1_SY1000_CR0,0,1553,1000_AL_.jpg"
            ) ,
            genre = "Biography, Comedy, Crime",
            actors = "Leonardo DiCaprio, Jonah Hill, Margot Robbie, Matthew McConaughey",
            plot = "Based on the true story of Jordan Belfort, from his rise to a wealthy stock-broker living the high life to his fall involving crime, corruption and the federal government.",
            poster = "https://m.media-amazon.com/images/M/MV5BMjIxMjgxNTk0MF5BMl5BanBnXkFtZTgwNjIyOTg2MDE@._V1_FMjpg_UX1000_.jpg",
            rating = "8.2",
        ) ,
        Movie(
            id = "105",
            title ="Interstellar",
            year =  "2014",
            director =  "Christopher Nolan",
            images = listOf(
                "https://images-na.ssl-images-amazon.com/images/M/MV5BMjA3NTEwOTMxMV5BMl5BanBnXkFtZTgwMjMyODgxMzE@._V1_SX1500_CR0,0,1500,999_AL_.jpg",
                "https://images-na.ssl-images-amazon.com/images/M/MV5BMzQ5ODE2MzEwM15BMl5BanBnXkFtZTgwMTMyODgxMzE@._V1_SX1500_CR0,0,1500,999_AL_.jpg",
                "https://images-na.ssl-images-amazon.com/images/M/MV5BMTg4Njk4MzY0Nl5BMl5BanBnXkFtZTgwMzIyODgxMzE@._V1_SX1500_CR0,0,1500,999_AL_.jpg",
                "https://images-na.ssl-images-amazon.com/images/M/MV5BMzE3MTM0MTc3Ml5BMl5BanBnXkFtZTgwMDIyODgxMzE@._V1_SX1500_CR0,0,1500,999_AL_.jpg",
                "https://images-na.ssl-images-amazon.com/images/M/MV5BNjYzNjE2NDk3N15BMl5BanBnXkFtZTgwNzEyODgxMzE@._V1_SX1500_CR0,0,1500,999_AL_.jpg"
            ) ,
            genre = "Adventure, Drama, Sci-Fi",
            actors = "Ellen Burstyn, Matthew McConaughey, Mackenzie Foy, John Lithgow",
            plot = "A team of explorers travel through a wormhole in space in an attempt to ensure humanity's survival.",
            poster = "https://m.media-amazon.com/images/M/MV5BYzdjMDAxZGItMjI2My00ODA1LTlkNzItOWFjMDU5ZDJlYWY3XkEyXkFqcGc@._V1_FMjpg_UX1000_.jpg",
            rating = "8.6",

            )

    )
}


Step 5: Setup Home Screen

Navigate to app > kotlin+java > {package-name} > MainActivity.kt and create a composable to setup the home screen. In the home screen, setup a LazyColumn that navigates to the details screen on click. To design the items of the LazyColumn, create a kotlin file with the name MovieRow.kt and create a composable. This composable will contain an Image of the movie poster on the left and three texts on the right which mentions the Movie Title, Genre and Rating.

MainActivity.kt
package com.geeksforgeeks.demo

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material3.*
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.navigation.NavController

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            // Set the UI content using Compose and apply Material theme
            MaterialTheme {
                // Entry point to navigation
                MovieNavigation()
            }
        }
    }
}

@Composable
fun MovieListScreen(navController: NavController, movieList: List<Movie> = getMovies()) {
    // Column to add padding around the movie list
    Column(modifier = Modifier.padding(12.dp)) {
        // LazyColumn to display a scrollable list of movies
        LazyColumn {
            items(movieList) { movie ->
                // Reusable row component for each movie
                MovieRow(movie = movie) { movieId ->
                    // Navigate to the DetailsScreen when a movie is clicked
                    navController.navigate(route = MovieScreens.DetailsScreen.name + "/$movieId")
                }
            }
        }
    }
}
MovieRow.kt
package com.geeksforgeeks.demo

import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.material3.*
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import coil.compose.AsyncImage

@Composable
fun MovieRow(movie: Movie, onItemClick: (String) -> Unit) {
    // Card representing a single movie item
    Card(
        modifier = Modifier
            .padding(8.dp)
            .fillMaxWidth()
            .clickable { onItemClick(movie.id) }, // Navigate on click
        colors = CardDefaults.cardColors(containerColor = Color.LightGray)
    ) {
        Row(modifier = Modifier.padding(16.dp)) {
            // Movie poster image
            AsyncImage(
                model = movie.poster,
                contentDescription = movie.title,
                modifier = Modifier.height(100.dp)
            )
            Spacer(modifier = Modifier.width(10.dp))

            // Movie details: title, genre, rating
            Column {
                Text(text = movie.title, style = MaterialTheme.typography.titleMedium)
                Text(text = "Genre: ${movie.genre}", style = MaterialTheme.typography.bodySmall)
                Text(text = "Rating: ${movie.rating}", style = MaterialTheme.typography.bodySmall)
            }
        }
    }
}


Step 6: Setup Details Screen

Create a new kotlin file to setup the Details Screen of movies. Set the name of the file and composable as DetailsScreen.kt. The composable will contain a Top Bar with a back stack navigation and the details of the movies. Use a LazyHorizontalGrid to display multiple images of a movie in Horizontal Grid with 2 maximum rows.

DetailsScreen.kt:

DetailsScreen.kt
package com.geeksforgeeks.demo

import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.grid.*
import androidx.compose.foundation.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material3.*
import androidx.compose.runtime.Composable
import androidx.compose.ui.*
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.unit.dp
import androidx.navigation.NavController
import coil.compose.AsyncImage

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun DetailsScreen(navController: NavController, movieId: String?) {
    // Find the movie based on passed ID
    val movie = getMovies().firstOrNull { it.id == movieId }

    // Show fallback text if movie not found
    if (movie == null) {
        Text("Movie not found")
        return
    }

    Scaffold(
        topBar = {
            TopAppBar(
                title = { Text("Movies", modifier = Modifier.padding(start = 8.dp)) },
                navigationIcon = {
                    // Back navigation icon
                    Icon(
                        imageVector = Icons.AutoMirrored.Filled.ArrowBack,
                        contentDescription = "Back",
                        modifier = Modifier
                            .clickable { navController.popBackStack() }
                            .padding(12.dp)
                    )
                }
            )
        }
    ) { paddingValues ->
        Column(
            modifier = Modifier
                .padding(paddingValues)
                .verticalScroll(rememberScrollState())
                .fillMaxSize()
        ) {
            // Movie poster and title section
            Column(
                modifier = Modifier.fillMaxWidth(),
                horizontalAlignment = Alignment.CenterHorizontally
            ) {
                AsyncImage(
                    model = movie.poster,
                    contentDescription = movie.title,
                    modifier = Modifier.height(200.dp)
                )
                Spacer(modifier = Modifier.height(8.dp))
                Text(
                    text = movie.title,
                    style = MaterialTheme.typography.headlineSmall,
                    fontWeight = androidx.compose.ui.text.font.FontWeight.Bold
                )
            }

            // Divider between poster and movie details
            HorizontalDivider(thickness = 1.dp, color = Color.LightGray, modifier = Modifier.padding(12.dp))

            // Movie details (text info)
            Column(modifier = Modifier.padding(12.dp, 0.dp)) {
                Text(text = "Year: ${movie.year}", style = MaterialTheme.typography.labelLarge)
                Text(text = "Genre: ${movie.genre}", style = MaterialTheme.typography.labelLarge)
                Text(text = "Rating: ${movie.rating}", style = MaterialTheme.typography.labelLarge)
                Text(text = "Actors: ${movie.actors}", style = MaterialTheme.typography.labelLarge)
                Text(text = "Director: ${movie.director}", style = MaterialTheme.typography.labelLarge)
                Text(text = "Plot: ${movie.plot}", style = MaterialTheme.typography.labelLarge)
                Spacer(modifier = Modifier.height(8.dp))
            }

            // Section title for images
            Text(
                text = "Movie Images",
                style = MaterialTheme.typography.headlineSmall,
                modifier = Modifier.padding(12.dp, 0.dp)
            )

            // Horizontal grid of additional movie images
            HorizontalScrollableImageGrid(listOf(movie))
        }
    }
}

@Composable
fun HorizontalScrollableImageGrid(movies: List<Movie>) {
    val images = movies[0].images

    // Lazy horizontal grid displaying movie images
    LazyHorizontalGrid(
        rows = GridCells.Fixed(2),
        modifier = Modifier.height(300.dp),
        horizontalArrangement = Arrangement.spacedBy(8.dp),
        verticalArrangement = Arrangement.spacedBy(8.dp),
        contentPadding = PaddingValues(12.dp)
    ) {
        items(images.size) { index ->
            val image = images[index]
            Card(
                modifier = Modifier.size(240.dp, 140.dp),
                colors = CardDefaults.cardColors(containerColor = Color.White)
            ) {
                AsyncImage(
                    model = image,
                    contentDescription = "Movie Poster",
                    modifier = Modifier.fillMaxSize(),
                    contentScale = ContentScale.Crop
                )
            }
        }
    }
}

Output:


Refer to the following github repo to get the entire code: Movies-App-Jetpack-Compose


Article Tags :

Similar Reads