1. Before you begin
Compose for TV is the latest UI framework to develop apps that run on Android TV. It unlocks all the benefits of Jetpack Compose for TV apps, which makes it easier to build beautiful and functional UIs for your app. Some specific benefits of Compose for TV include the following:
- Flexibility. Compose can be used to create any type of UI from simple layouts to complex animations. Components work out of the box, but can also be customized and styled to fit your app's needs.
- Simplified and accelerated development. Compose is compatible with existing code and lets developers build apps with less code.
- Intuitiveness: Compose uses a declarative syntax that makes it intuitive to change your UI, and debug, understand, and review your code.
A common use case for TV apps is media consumption. Users browse content catalogs and select the content they want to watch. The content can be a movie, TV show, or podcast. After users select a piece of content, they might want to see more information about it, such as a short description, the playback length, and the names of the creators. In this codelab, you learn how to implement a catalog-browser screen and a details screen with Compose for TV.
Prerequisites
- Experience with Kotlin syntax, including lambdas.
- Basic experience with Compose. If you're unfamiliar with Compose, complete the Jetpack Compose basics codelab.
- Basic knowledge of composables and modifiers.
What you build
- A video-player app with a catalog-browser screen and a details screen.
- A catalog-browser screen that shows a list of videos for users to choose. It looks like the following image:
- A details screen that shows the metadata of a selected video, such as the title, description, and length. It looks like the following image:
What you need
- The latest version of Android Studio
2. Get set up
To get the code that contains theming and basic setup for this codelab, do one of the following:
- Clone the code from this GitHub repository:
$ git clone https://github.com/android/tv-codelabs.git
The main
branch contains the starter code and the solution
branch contains the solution code.
- Download the
main.zip
file, which contains the starter code, and thesolution.zip
file, which contains the solution code.
Now that you've downloaded the code, open the IntroductionToComposeForTV project folder in Android Studio. You're now ready to get started.
3. Implement the catalog-browser screen
The catalog-browser screen lets users browse movie catalogs. You implement the catalog browser as a Composable
function. You can find the CatalogBrowser
Composable
function in the CatalogBrowser.kt
file. You implement the catalog-browser screen in this Composable
function.
The starter code has a ViewModel called the CatalogBrowserViewModel
class that has several attributes and methods to retrieve Movie
objects that describe movie content. You implement a catalog browser with retrieved Movie
objects.
CatalogBrowser.kt
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.tv.material3.ExperimentalTvMaterial3Api
import com.example.tvcomposeintroduction.data.Movie
@OptIn(ExperimentalTvMaterial3Api::class)
@Composable
fun CatalogBrowser(
modifier: Modifier = Modifier,
catalogBrowserViewModel: CatalogBrowserViewModel = viewModel(),
onMovieSelected: (Movie) -> Unit = {}
) {
}
Display the category names
You can access a list of categories with the catalogBrowserViewModel.categoryList
attribute, which is a flow of a Category
list. The flow is collected as a Compose State
object by calling its collectAsState
method. A Category
object has the name
attribute, which is a String
value that represents the category name.
To display the category names, follow these steps:
- In Android Studio, open the starter code's
CatalogBrowser.kt
file, and then add aTvLazyColumn
Composable
function to theCatalogBrowser
Composable
function. - Call the
catalogBrowserViewModel.categoryList.collectAsState()
method to collect the flow as aState
object. - Declare
categoryList
as a delegated property of theState
object that you created in the previous step. - Call the
items
function with thecategoryList
variable as a parameter. - Call the
Text
Composable
function with the category name as the parameter that's passed as an argument of the lambda.
CatalogBrowser.kt
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.tv.foundation.lazy.list.TvLazyColumn
import androidx.tv.foundation.lazy.list.items
import androidx.tv.material3.ExperimentalTvMaterial3Api
import androidx.tv.material3.Text
import com.example.tvcomposeintroduction.data.Movie
@OptIn(ExperimentalTvMaterial3Api::class)
@Composable
fun CatalogBrowser(
modifier: Modifier = Modifier,
catalogBrowserViewModel: CatalogBrowserViewModel = viewModel(),
onMovieSelected: (Movie) -> Unit = {}
) {
val categoryList by
catalogBrowserViewModel.categoryList.collectAsState()
TvLazyColumn(modifier = modifier) {
items(categoryList) { category ->
Text(text = category.name)
}
}
}
Display the content list for each category
A Category
object has another attribute called movieList
. The attribute is a list of Movie
objects that represent movies that belong to the category.
To display the content list for each category, follow these steps:
- Add the
TvLazyRow
Composable
function and then pass a lambda to it. - In the lambda, call the
items
function with thecategory
.movieList
attribute value and then pass a lambda to it. - In the lambda passed to the
items
function, call theMovieCard
Composable
function with aMovie
object.
CatalogBrowser.kt
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.tv.foundation.lazy.list.TvLazyColumn
import androidx.tv.foundation.lazy.list.TvLazyRow
import androidx.tv.foundation.lazy.list.items
import androidx.tv.material3.ExperimentalTvMaterial3Api
import androidx.tv.material3.Text
import com.example.tvcomposeintroduction.data.Movie
import com.example.tvcomposeintroduction.ui.components.MovieCard
@OptIn(ExperimentalTvMaterial3Api::class)
@Composable
fun CatalogBrowser(
modifier: Modifier = Modifier,
catalogBrowserViewModel: CatalogBrowserViewModel = viewModel(),
onMovieSelected: (Movie) -> Unit = {}
) {
val categoryList by
catalogBrowserViewModel.categoryList.collectAsState()
TvLazyColumn(modifier = modifier) {
items(categoryList) { category ->
Text(text = category.name)
TvLazyRow {
items(category.movieList) {movie ->
MovieCard(movie = movie)
}
}
}
}
}
Optional: Adjust the layout
- To set the gap between categories, pass an
Arrangement
object to theTvLazyColumn
Composable
function with theverticalArrangement
parameter. TheArrangement
object is created by calling theArrangement#spacedBy
method. - To set the gap between movie cards, pass an
Arrangement
object to theTvLazyRow
Composable
function with thehorizontalArrangement
parameter. - To set an indentation to the column, pass a
PaddingValue
object with thecontentPadding
parameter.
CatalogBrowser.kt
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.tv.foundation.lazy.list.TvLazyColumn
import androidx.tv.foundation.lazy.list.TvLazyRow
import androidx.tv.foundation.lazy.list.items
import androidx.tv.material3.ExperimentalTvMaterial3Api
import androidx.tv.material3.Text
import com.example.tvcomposeintroduction.data.Movie
import com.example.tvcomposeintroduction.ui.components.MovieCard
@OptIn(ExperimentalTvMaterial3Api::class)
@Composable
fun CatalogBrowser(
modifier: Modifier = Modifier,
catalogBrowserViewModel: CatalogBrowserViewModel = viewModel(),
onMovieSelected: (Movie) -> Unit = {}
) {
val categoryList by
catalogBrowserViewModel.categoryList.collectAsState()
TvLazyColumn(
modifier = modifier,
verticalArrangement = Arrangement.spacedBy(16.dp),
contentPadding = PaddingValues(horizontal = 48.dp, vertical = 32.dp)
) {
items(categoryList) { category ->
Text(text = category.name)
TvLazyRow(
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
items(category.movieList) { movie ->
MovieCard(movie = movie)
}
}
}
}
}
4. Implement the details screen
The details screen shows the details of the selected movie. There's a Details
Composable
function in the Details.kt
file. You add code to this function to implement the details screen.
Details.kt
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.tv.material3.ExperimentalTvMaterial3Api
import com.example.tvcomposeintroduction.data.Movie
@OptIn(ExperimentalTvMaterial3Api::class)
@Composable
fun Details(movie: Movie, modifier: Modifier = Modifier) {
}
Display the movie title, studio name, and description
A Movie
object has the following three string attributes as metadata of the movie:
title
. The movie title.studio
. The name of the studio that produced the movie.description
. A short summary of the movie.
To show this metadata on the details screen, follow these steps:
- Add a
Column
Composable
function, and then set 32 dp vertical and 48 dp horizontal clearance around the column with theModifier
object created by theModifier.padding
method. - Add a
Text
Composable
function to display the movie title. - Add a
Text
Composable
function to display the studio name. - Add a
Text
Composable
function to display the movie description.
Details.kt
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.tv.material3.ExperimentalTvMaterial3Api
import androidx.tv.material3.Text
import com.example.tvcomposeintroduction.data.Movie
@OptIn(ExperimentalTvMaterial3Api::class)
@Composable
fun Details(movie: Movie, modifier: Modifier = Modifier) {
Column(
modifier = Modifier
.padding(vertical = 32.dp, horizontal = 48.dp)
) {
Text(text = movie.title)
Text(text = movie.studio)
Text(text = movie.title)
}
}
The Modifier
object specified in the parameter of the Details
Composable
function is used in the next task.
Display the background image associated with a given Movie
object
A Movie
object has a backgroundImageUrl
attribute that indicates the location of the background image for the movie described by the object.
To display the background image for a given movie, follow these steps:
- Add a
Box
Composable
function as a wrapper of theColumn
Composable
function with themodifier
object passed through theDetails
Composable
function. - In the
Box
Composable
function, call thefillMaxSize
method of themodifier
object to make theBox
Composable
function fill the maximum size that can be allocated to theDetails
Composable
function. - Add an
AsyncImage
Composable
function with the following parameters to theBox
Composable
function:
- Set the
backgroundImageUrl
attribute's value of the givenMovie
object to amodel
parameter. - Pass
null
to acontentDescription
parameter.
- Pass a
ContentScale.Crop
object to acontentScale
parameter. To see the differentContentScale
options, see Content scale. - Pass the return value of the
Modifier.fillMaxSize
method to themodifier
parameter. - Set 32 dp vertical and 48 dp horizontal clearance to the column by setting a
Modifier
object created by calling theModifier.padding
method.
Details.kt
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.unit.dp
import androidx.tv.material3.ExperimentalTvMaterial3Api
import androidx.tv.material3.Text
import coil.compose.AsyncImage
import com.example.tvcomposeintroduction.data.Movie
@OptIn(ExperimentalTvMaterial3Api::class)
@Composable
fun Details(movie: Movie, modifier: Modifier = Modifier) {
Box(modifier = modifier.fillMaxSize()) {
AsyncImage(
model = movie.cardImageUrl,
contentDescription = null,
contentScale = ContentScale.Crop,
modifier = Modifier.fillMaxSize()
)
Column(
modifier = Modifier
.padding(vertical = 32.dp, horizontal = 48.dp)
) {
Text(
text = movie.title,
)
Text(
text = movie.studio,
)
Text(
text = movie.title,
)
}
}
}
Refer to the MaterialTheme
object for consistent theming
The MaterialTheme
object contains functions to reference current theme values, such as those in the Typography
and [ColorScheme
][ColorScheme] classes.
To refer to the MaterialTheme
object for consistent theming, follow these steps:
- Set the
MaterialTheme.typography.headlineLarge
property to the text style of the movie title. - Set the
MaterialTheme.typography.headlineMedium
property to the text style of the other twoText
Composable
functions. - Set the
MaterialTheme.colorScheme.background
property to the background color of theColumn
Composable
function with theModifier.background
method.
[ColorScheme]: /reference/kotlin/androidx/tv/material3/ColorScheme)
Details.kt
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.unit.dp
import androidx.tv.material3.ExperimentalTvMaterial3Api
import androidx.tv.material3.MaterialTheme
import androidx.tv.material3.Text
import coil.compose.AsyncImage
import com.example.tvcomposeintroduction.data.Movie
@OptIn(ExperimentalTvMaterial3Api::class)
@Composable
fun Details(movie: Movie, modifier: Modifier = Modifier) {
Box(modifier = modifier.fillMaxSize()) {
AsyncImage(
model = movie.cardImageUrl,
contentDescription = null,
contentScale = ContentScale.Crop,
modifier = Modifier.fillMaxSize()
)
Column(
modifier = Modifier
.padding(vertical = 32.dp, horizontal = 48.dp)
) {
Text(
text = movie.title,
style = MaterialTheme.typography.headlineLarge,
)
Text(
text = movie.studio,
style = MaterialTheme.typography.headlineMedium,
)
Text(
text = movie.title,
style = MaterialTheme.typography.headlineMedium,
)
}
}
}
5. Add navigation between the screens
Now you have the catalog-browser and details screens. After a user selects content on the catalog-browser screen, the screen must transition to the details screen. To make this possible, you use the clickable
modifier to add an event
listener to the MovieCard
Composable
function. When the center button of the directional pad is pressed, the CatalogBrowserViewModel#showDetails
method is called with the movie object associated with the MovieCard
Composable
function as an argument.
- Open the
com.example.tvcomposeintroduction.ui.screens.CatalogBrowser
file. - Pass a lambda function to the
MovieCard
Composable
function with anonClick
parameter. - Call the
onMovieSelected
callback with the movie object associated with theMovieCard
Composable
function.
CatalogBrowser.kt
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.tv.foundation.lazy.list.TvLazyColumn
import androidx.tv.foundation.lazy.list.TvLazyRow
import androidx.tv.foundation.lazy.list.items
import androidx.tv.material3.ExperimentalTvMaterial3Api
import androidx.tv.material3.Text
import com.example.tvcomposeintroduction.data.Movie
import com.example.tvcomposeintroduction.ui.components.MovieCard
@Composable
fun CatalogBrowser(
modifier: Modifier = Modifier,
catalogBrowserViewModel: CatalogBrowserViewModel = viewModel(),
onMovieSelected: (Movie) -> Unit = {}
) {
val categoryList by
catalogBrowserViewModel.categoryList.collectAsState()
TvLazyColumn(
modifier = modifier,
verticalArrangement = Arrangement.spacedBy(16.dp),
contentPadding = PaddingValues(horizontal = 48.dp, vertical = 32.dp)
) {
items(categoryList) { category ->
Text(text = category.name)
TvLazyRow(
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
items(category.movieList) { movie ->
MovieCard(movie = movie, onClick = { onMovieSelected(movie) })
}
}
}
}
}
6. Add a carousel to the catalog-browser screen to highlight featured content
Carousel is a commonly adapted UI component that automatically updates its slides after a specified duration. It's commonly used to highlight featured content.
To add a carousel to the catalog-browser screen to highlight movies in the featured content list, follow these steps:
- Open the
com.example.tvcomposeintroduction.ui.screens.CatalogBrowser
file. - Call the
item
function to add an item to theTvLazyColumn
Composable
function. - Declare
featuredMovieList
as a delegated property in the lambda passed to theitem
function and then set theState
object to be delegated, which is collected from thecatalogBrowserViewModel.featuredMovieList
attribute. - Call the
Carousel
Composable
function inside theitem
function and then pass in the following parameters:
- The size of the
featuredMovieList
variable through aslideCount
parameter. - A
Modifier
object to specify carousel size with theModifier.fillMaxWidth
andModifier.height
methods. TheCarousel
Composable
function uses 376 dp of the height by passing a376.dp
value to theModifier.height
method. - A lambda called with an integer value that indicates the index of the visible carousel item.
- Retrieve the
Movie
object from thefeaturedMovieList
variable and the given index value. - Add a
CarouselSlide
Composable
function to theCarousel
Composable
function. - Add a
Text
Composable
function to theCarouselSlide
Composable
function to display the movie title.
CatalogBrowser.kt
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.tv.foundation.lazy.list.TvLazyColumn
import androidx.tv.foundation.lazy.list.TvLazyRow
import androidx.tv.foundation.lazy.list.items
import androidx.tv.material3.Carousel
import androidx.tv.material3.ExperimentalTvMaterial3Api
import androidx.tv.material3.Text
import com.example.tvcomposeintroduction.data.Movie
import com.example.tvcomposeintroduction.ui.components.MovieCard
@OptIn(ExperimentalTvMaterial3Api::class)
@Composable
fun CatalogBrowser(
modifier: Modifier = Modifier,
catalogBrowserViewModel: CatalogBrowserViewModel = viewModel(),
onMovieSelected: (Movie) -> Unit = {}
) {
val categoryList by
catalogBrowserViewModel.categoryList.collectAsState()
TvLazyColumn(
modifier = modifier,
verticalArrangement = Arrangement.spacedBy(16.dp),
contentPadding = PaddingValues(horizontal = 48.dp, vertical = 32.dp)
) {
item {
val featuredMovieList by catalogBrowserViewModel.featuredMovieList.collectAsState()
Carousel(
slideCount = featuredMovieList.size,
modifier = Modifier
.fillMaxWidth()
.height(376.dp)
) { indexOfCarouselSlide ->
val featuredMovie =
featuredMovieList[indexOfCarouselSlide]
CarouselSlide {
Text(text = featuredMovie.title)
}
}
}
items(categoryList) { category ->
Text(text = category.name)
TvLazyRow(
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
items(category.movieList) { movie ->
MovieCard(movie = movie, onClick = { onMovieSelected(movie) })
}
}
}
}
}
Display background images
The CarouselSlide
Composable
function can take another lambda to specify how the background of the CarouselSlide
Composable
function is displayed.
To display background images, follow these steps:
- Pass a lambda to the
CarouselSlide
Composable
function with thebackground
parameter. - Call the
AsyncImage
Composable
function to load the background image associated with theMovie
object to the background of theCarouselSlide
Composable
function. - Update the position and text style of the
Text
Composable
function in theCarouselSlide
Composable
function for better visibility. - Set a placeholder to the
AsyncImage
Composable
function to avoid layout shift. The starter code has a placeholder as a drawable that you can reference with theR.drawable.placeholder
.
CatalogBrowser.kt
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.tv.foundation.lazy.list.TvLazyColumn
import androidx.tv.foundation.lazy.list.TvLazyRow
import androidx.tv.foundation.lazy.list.items
import androidx.tv.material3.Carousel
import androidx.tv.material3.ExperimentalTvMaterial3Api
import androidx.tv.material3.Text
import coil.compose.AsyncImage
import com.example.tvcomposeintroduction.R
import com.example.tvcomposeintroduction.data.Movie
import com.example.tvcomposeintroduction.ui.components.MovieCard
@OptIn(ExperimentalTvMaterial3Api::class)
@Composable
fun CatalogBrowser(
modifier: Modifier = Modifier,
catalogBrowserViewModel: CatalogBrowserViewModel = viewModel(),
onMovieSelected: (Movie) -> Unit = {}
) {
val categoryList by
catalogBrowserViewModel.categoryList.collectAsState()
TvLazyColumn(
modifier = modifier,
verticalArrangement = Arrangement.spacedBy(16.dp),
contentPadding = PaddingValues(horizontal = 48.dp, vertical = 32.dp)
) {
item {
val featuredMovieList by catalogBrowserViewModel.featuredMovieList.collectAsState()
Carousel(
slideCount = featuredMovieList.size,
modifier = Modifier
.fillMaxWidth()
.height(376.dp),
) { indexOfCarouselItem ->
val featuredMovie = featuredMovieList[indexOfCarouselItem]
CarouselSlide(
background = {
AsyncImage(
model = featuredMovie.backgroundImageUrl,
contentDescription = null,
placeholder = painterResource(
id = R.drawable.placeholder
),
contentScale = ContentScale.Crop,
modifier = Modifier.fillMaxSize(),
)
},
) {
Text(text = featuredMovie.title)
}
}
}
items(categoryList) { category ->
Text(text = category.name)
TvLazyRow(
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
items(category.movieList) { movie ->
MovieCard(movie = movie, onClick = { onMovieSelected(movie) })
}
}
}
}
}
Add a screen transition to the details screen
You can let users click the CarouselSlide
Composable
function.
To let users see the details of the movie in the visible carousel item on the details screen, follow these steps:
- Pass the return value of
Modifier.clickable
method to theCarouselSlide
Composable
function through themodifier
parameter. - Call the
onMovieSelected
function with theMovie
object for the visibleCarouselSlide
Composable
function in the lambda passed to theModifier.clickable
method.
CatalogBrowser.kt
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.tv.foundation.lazy.list.TvLazyColumn
import androidx.tv.foundation.lazy.list.TvLazyRow
import androidx.tv.foundation.lazy.list.items
import androidx.tv.material3.Carousel
import androidx.tv.material3.ExperimentalTvMaterial3Api
import androidx.tv.material3.Text
import coil.compose.AsyncImage
import com.example.tvcomposeintroduction.R
import com.example.tvcomposeintroduction.data.Movie
import com.example.tvcomposeintroduction.ui.components.MovieCard
@OptIn(ExperimentalTvMaterial3Api::class)
@Composable
fun CatalogBrowser(
modifier: Modifier = Modifier,
catalogBrowserViewModel: CatalogBrowserViewModel = viewModel(),
onMovieSelected: (Movie) -> Unit = {}
) {
val categoryList by
catalogBrowserViewModel.categoryList.collectAsState()
TvLazyColumn(
modifier = modifier,
verticalArrangement = Arrangement.spacedBy(16.dp),
contentPadding = PaddingValues(horizontal = 48.dp, vertical = 32.dp)
) {
item {
val featuredMovieList by catalogBrowserViewModel.featuredMovieList.collectAsState()
Carousel(
slideCount = featuredMovieList.size,
modifier = Modifier
.fillMaxWidth()
.height(376.dp),
) { indexOfCarouselItem ->
val featuredMovie = featuredMovieList[indexOfCarouselItem]
CarouselSlide(
background = {
AsyncImage(
model = featuredMovie.backgroundImageUrl,
contentDescription = null,
placeholder = painterResource(
id = R.drawable.placeholder
),
contentScale = ContentScale.Crop,
modifier = Modifier.fillMaxSize(),
)
},
modifier = Modifier.clickable { onMovieSelected(featuredMovie) }
) {
Text(text = featuredMovie.title)
}
}
}
items(categoryList) { category ->
Text(text = category.name)
TvLazyRow(
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
items(category.movieList) { movie ->
MovieCard(movie = movie, onClick = { onMovieSelected(movie) })
}
}
}
}
}
7. Get the solution code
To download the solution code for this codelab, do one of the following:
- Click the following button to download it as a zip file, and then unzip it and open it in Android Studio.
- Retrieve it with Git:
$ git clone https://github.com/android/tv-codelabs.git $ cd tv-codelabs $ git checkout solution $ cd IntroductionToComposeForTV
8. Congratulations.
Congratulations! You learned the basics of Compose for TV:
- How to implement a screen to show a content list by combining TvLazyColumn and TvLazyLow.
- Basic screen implementation to show content details.
- How to add screen transitions between the two screens.