1. Antes de começar
Compose para TV é o framework de interface mais recente para desenvolver apps executados no Android TV. Ele oferece todos os benefícios do Jetpack Compose para apps de TV, o que facilita a criação de interfaces incríveis e funcionais para seu app. Confira alguns benefícios específicos do Compose para TV:
- Flexibilidade. O Compose pode ser usado para criar qualquer tipo de interface, desde layouts simples a animações complexas. Os componentes funcionam imediatamente, mas também podem ser personalizados e estilizados para atender às necessidades do seu app.
 - Desenvolvimento simplificado e acelerado. O Compose é compatível com códigos já existentes e permite que os desenvolvedores criem apps com menos código.
 - Intuição: o Compose usa uma sintaxe declarativa que torna intuitivas a mudança da interface e a depuração, compreensão e revisão do código.
 
Um caso de uso comum para apps de TV é o consumo de mídia. Os usuários navegam por catálogos de conteúdo e selecionam aquele que querem assistir. O conteúdo pode ser um filme, um programa de TV ou um podcast. Depois de selecionar um conteúdo, os usuários podem conferir mais informações sobre ele, por exemplo, uma descrição curta, a duração da reprodução e o nome dos criadores. Neste codelab, você vai aprender a implementar uma tela para um navegador de catálogo e uma tela de detalhes com o Compose para TV.
Pré-requisitos
- Experiência com a sintaxe do Kotlin, incluindo lambdas.
 - Experiência básica com o Compose. Se você não conhece o Compose, conclua o codelab Noções básicas do Jetpack Compose.
 - Conhecimento básico de combináveis e modificadores.
 - Qualquer um destes dispositivos para executar o app de exemplo:
- Um dispositivo Android TV
 - Um dispositivo virtual Android com um perfil na categoria de definição de dispositivo de TV
 
 
O que você vai criar
- Um app de player de vídeo com uma tela de navegador de catálogo e uma tela de detalhes.
 - Uma tela de navegador de catálogo que mostra uma lista de vídeos para os usuários escolherem. Ela tem a seguinte aparência:
 

- Uma tela de detalhes que mostra os metadados de um vídeo selecionado, por exemplo, título, descrição e duração. Ela tem a seguinte aparência:
 

O que é necessário
- A versão mais recente do Android Studio
 - Um dispositivo Android TV ou virtual na categoria de dispositivo de TV
 
2. Começar a configuração
Para receber o código que contém a configuração básica e de aplicação de temas para este codelab, siga um destes procedimentos:
- Clone o código deste repositório do GitHub:
 
$ git clone https://github.com/android/tv-codelabs.git
A ramificação main contém o código inicial, e a ramificação solution contém o código da solução.
- Faça o download do arquivo 
main.zip, que contém o código inicial, e do arquivosolution.zip, que contém o código da solução. 
Depois de fazer o download do código, abra a pasta do projeto IntroductionToComposeForTV no Android Studio. Está tudo pronto para começar.
3. Implementar a tela do navegador de catálogo
A tela do navegador de catálogo permite que os usuários procurem catálogos de filmes. Implemente a tela do navegador de catálogo como uma função combinável. A função combinável CatalogBrowser está no arquivo CatalogBrowser.kt. Implemente a tela do navegador de catálogo nesta função combinável.
O código inicial tem um ViewModel conhecido como a classe CatalogBrowserViewModel que tem vários atributos e métodos para extrair objetos Movie que descrevem o conteúdo do filme. Você implementa um navegador de catálogo com objetos Movie recuperados.
CatalogBrowser.kt
@OptIn(ExperimentalTvMaterial3Api::class)
@Composable
fun CatalogBrowser(
    modifier: Modifier = Modifier,
    catalogBrowserViewModel: CatalogBrowserViewModel = hiltViewModel(),
    onMovieSelected: (Movie) -> Unit = {}
) {
}
Mostrar os nomes das categorias
É possível acessar uma lista de categorias com o atributo catalogBrowserViewModel.categoryList, que é um fluxo de uma lista de categorias (Category). O fluxo é coletado como um objeto Compose State chamando o método collectAsStateWithLifecycle dele. Um objeto Category tem o atributo name, que é um valor String que representa o nome da categoria.
Para mostrar os nomes das categorias, siga estas etapas:
- No Android Studio, abra o arquivo 
CatalogBrowser.ktdo código inicial e adicione uma função combinávelLazyColumnà função combinávelCatalogBrowser. - Chame o método 
catalogBrowserViewModel.categoryList.collectAsStateWithLifeCycle()para coletar o fluxo como um objetoState. - Declare 
categoryListcomo uma propriedade delegada do objetoStateque você criou na etapa anterior. - Chame a função 
itemscom a variávelcategoryListcomo parâmetro. - Chame a função combinável 
Textcom o nome da categoria como o parâmetro transmitido como um argumento da lambda. 
CatalogBrowser.kt
@OptIn(ExperimentalTvMaterial3Api::class)
@Composable
fun CatalogBrowser(
    modifier: Modifier = Modifier,
    catalogBrowserViewModel: CatalogBrowserViewModel = hiltViewModel(),
    onMovieSelected: (Movie) -> Unit = {}
) {
    val categoryList by catalogBrowserViewModel.categoryList.collectAsStateWithLifecycle()
    LazyColumn(modifier = modifier) {
        items(categoryList) { category ->
            Text(text = category.name)
        }
    }
}
Mostrar a lista de conteúdo de cada categoria
Um objeto Category tem outro atributo chamado movieList. O atributo é uma lista de objetos Movie que representam os filmes que pertencem à categoria.
Para mostrar a lista de conteúdos de cada categoria, siga estas etapas:
- Adicione a função combinável 
LazyRowe transmita uma lambda a ela. - Na lambda, chame a função 
itemscom o valor de atributocategory.movieListe, em seguida, transmita uma lambda a ela. - Na lambda transmitida à função 
items, chame a função combinávelMovieCardcom um objetoMovie. 
CatalogBrowser.kt
@OptIn(ExperimentalTvMaterial3Api::class)
@Composable
fun CatalogBrowser(
    modifier: Modifier = Modifier,
    catalogBrowserViewModel: CatalogBrowserViewModel = hiltViewModel(),
    onMovieSelected: (Movie) -> Unit = {}
) {
    val categoryList by
    catalogBrowserViewModel.categoryList.collectAsStateWithLifecycle()
    LazyColumn(modifier = modifier) {
        items(categoryList) { category ->
            Text(text = category.name)
            LazyRow {
                items(category.movieList) {movie ->
                    MovieCard(movie = movie)
                }
            }
        }
    }
}
Opcional: ajustar o layout
- Para definir a lacuna entre as categorias, transmita um objeto 
Arrangementà função combinávelLazyColumncom o parâmetroverticalArrangement. O objetoArrangementé criado chamando o métodoArrangement#spacedBy. - Para definir a lacuna entre os cartões de filmes, transmita um objeto 
Arrangementà função combinávelLazyRowcom o parâmetrohorizontalArrangement. - Para definir um recuo na coluna, transmita um objeto 
PaddingValuecom o parâmetrocontentPadding. 
CatalogBrowser.kt
@OptIn(ExperimentalTvMaterial3Api::class)
@Composable
fun CatalogBrowser(
    modifier: Modifier = Modifier,
    catalogBrowserViewModel: CatalogBrowserViewModel = hiltViewModel(),
    onMovieSelected: (Movie) -> Unit = {}
) {
    val categoryList by
    catalogBrowserViewModel.categoryList.collectAsStateWithLifeCycle()
    LazyColumn(
        modifier = modifier,
        verticalArrangement = Arrangement.spacedBy(16.dp),
        contentPadding = PaddingValues(horizontal = 48.dp, vertical = 32.dp)
    ) {
        items(categoryList) { category ->
            Text(text = category.name)
            LazyRow(
                horizontalArrangement = Arrangement.spacedBy(8.dp)
            ) {
                items(category.movieList) { movie ->
                    MovieCard(movie = movie)
                }
            }
        }
    }
}
4. Implementar a tela de detalhes
A tela de detalhes mostra os detalhes do filme selecionado. Há uma função combinável Details no arquivo Details.kt. Adicione o código a essa função para implementar a tela de detalhes.
Details.kt
@Composable
fun Details(movie: Movie, modifier: Modifier = Modifier) {
}
Mostrar o título, o nome do estúdio e a descrição do filme
Um objeto Movie tem estes três atributos de string como metadados do filme:
title: o título do filme.studio: o nome do estúdio que produziu o filme.description: um breve resumo do filme.
Para mostrar esses metadados na tela de detalhes, siga estas etapas:
- Adicione uma função combinável 
Columne defina a área livre vertical como 32 dp e a horizontal como 48 dp ao redor da coluna com o objetoModifiercriado pelo métodoModifier.padding. - Adicione uma função combinável 
Textpara mostrar o título do filme. - Adicione uma função combinável 
Textpara mostrar o nome do estúdio. - Adicione uma função combinável 
Textpara mostrar a descrição do filme. 
Details.kt
@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.description)
    }
}
O objeto Modifier especificado no parâmetro da função combinável Details é usado na próxima tarefa.
Mostrar a imagem de plano de fundo associada a um determinado objeto Movie
Um objeto Movie tem um atributo backgroundImageUrl que indica o local da imagem de plano de fundo do filme descrito pelo objeto.
Para mostrar a imagem de plano de fundo de um determinado filme, siga estas etapas:
- Adicione uma função combinável 
Boxcomo um wrapper da função combinávelColumncom o objetomodifiertransmitido pela função combinávelDetails. - Na função combinável 
Box, chame o métodofillMaxSizedo objetomodifierpara que a função combinávelBoxpreencha o tamanho máximo que pode ser alocado para a função combinávelDetails. - Adicione uma função combinável 
AsyncImageà funçãoBoxcom os parâmetros abaixo: 
- Define o valor do atributo 
backgroundImageUrldo objetoMovieespecificado como um parâmetromodel. - Transmita 
nulla um parâmetrocontentDescription. 
- Transmita um objeto 
ContentScale.Cropa um parâmetrocontentScale. Para conferir as diferentes opções deContentScale, consulte Escala de conteúdo. - Transmita o valor de retorno do método 
Modifier.fillMaxSizeao parâmetromodifier. 
Details.kt
@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 {
            Text(
                text = movie.title,
            )
            Text(
                text = movie.studio,
            )
            Text(text = movie.description)
        }
    }
}
Consultar o objeto MaterialTheme para obter uma aplicação consistente de temas
O objeto MaterialTheme contém funções para indicar valores de tema atuais, por exemplo, os das classes Typography e ColorScheme.
Para consultar o objeto MaterialTheme e ter uma aplicação consistente de temas, siga estas etapas:
- Defina a propriedade 
MaterialTheme.typography.displayMediumcomo o estilo de texto do título do filme. - Defina a propriedade 
MaterialTheme.typography.bodySmallcomo o estilo de texto da segunda função combinávelText. - Defina a propriedade 
MaterialTheme.colorScheme.backgroundcomo a cor de fundo da funçãoColumncom o métodoModifier.background. 
Details.kt
@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
                .background(MaterialTheme.colorScheme.background),
        ) {
            Text(
                text = movie.title,
                style = MaterialTheme.typography.displayMedium,
            )
            Text(
                text = movie.studio,
                style = MaterialTheme.typography.bodySmall,
            )
            Text(text = movie.description)
        }
    }
}
Opcional: ajustar o layout
Para ajustar o layout da função combinável Details, siga estas etapas:
- Defina a função combinável 
Boxpara usar todo o espaço disponível com o modificadorfillMaxSize. - Defina o segundo plano da função combinável 
Boxcom o modificadorbackgroundpara preencher o segundo plano com um gradiente linear criado ao chamar a funçãoBrush.linearGradientcom uma lista de objetosColorcontendo o valorMaterialTheme.colorScheme.backgroundeColor.Transparent - Defina a área livre horizontal 
48.dpe vertical24.dpao redor da função combinávelColumncom o modificadorpadding - Defina a largura da função combinável 
Columncom o modificadorwidth, que é criado chamando a funçãoModifier.widthcom o valor0.5f. - Adicione o espaçamento 
8.dpentre a segunda função combinávelTexte o terceiro combinávelTextusandoSpacer. A altura da função combinávelSpaceré especificada com o modificadorheight, que é criado com a funçãoModifier.height. 
Details.kt
@Composable
fun Details(movie: Movie, modifier: Modifier = Modifier) {
    Box(modifier = modifier.fillMaxSize()) {
        AsyncImage(
            model = movie.cardImageUrl,
            contentDescription = null,
            contentScale = ContentScale.Crop,
            modifier = Modifier.fillMaxSize()
        )
        Box(
            modifier = Modifier
                .background(
                    Brush.linearGradient(
                        listOf(
                            MaterialTheme.colorScheme.background,
                            Color.Transparent
                        )
                    )
                )
                .fillMaxSize()
        ) {
            Column(
                modifier = Modifier
                    .padding(horizontal = 48.dp, vertical = 24.dp)
                    .fillMaxWidth(0.5f)
            ) {
                Text(
                    text = movie.title,
                    style = MaterialTheme.typography.displayMedium,
                )
                Text(
                    text = movie.studio,
                    style = MaterialTheme.typography.bodySmall,
                )
                Spacer(modifier = Modifier.height(8.dp))
                Text(
                    text = movie.description,
                )
            }
        }
    }
}
5. Adicionar navegação entre as telas
Agora você tem a tela do navegador de catálogo e as telas de detalhes. Depois que um usuário seleciona o conteúdo na tela do navegador de catálogo, é necessário mudar para a tela de detalhes. Para que isso seja possível, use o modificador clickable para adicionar um listener event à função combinável MovieCard. Quando o botão central do botão direcional é pressionado, o método CatalogBrowserViewModel#showDetails é chamado, apresentando o objeto do filme associado à função combinável MovieCard como um argumento.
- Abra o arquivo 
com.example.tvcomposeintroduction.ui.screens.CatalogBrowser. - Transmita uma função lambda à função combinável 
MovieCardcom um parâmetroonClick. - Chame o callback 
onMovieSelectedcom o objeto do filme associado à função combinávelMovieCard. 
CatalogBrowser.kt
@Composable
fun CatalogBrowser(
    modifier: Modifier = Modifier,
    catalogBrowserViewModel: CatalogBrowserViewModel = hiltViewModel(),
    onMovieSelected: (Movie) -> Unit = {}
) {
    val categoryList by
    catalogBrowserViewModel.categoryList.collectAsStateWithLifecycle()
    LazyColumn(
        modifier = modifier,
        verticalArrangement = Arrangement.spacedBy(16.dp),
        contentPadding = PaddingValues(horizontal = 48.dp, vertical = 32.dp)
    ) {
        items(categoryList) { category ->
            Text(text = category.name)
            LazyRow(
                horizontalArrangement = Arrangement.spacedBy(8.dp)
            ) {
                items(category.movieList) { movie ->
                    MovieCard(movie = movie, onClick = { onMovieSelected(movie) })
                }
            }
        }
    }
}
6. Adicionar um carrossel à tela do navegador de catálogo para realçar o conteúdo em destaque
O carrossel é um componente da interface comumente adaptado que atualiza automaticamente os slides após uma duração específica. É usado normalmente para realçar o conteúdo em destaque.
Para adicionar um carrossel à tela do navegador de catálogo e realçar filmes na lista de conteúdo em destaque, siga estas etapas:
- Abra o arquivo 
com.example.tvcomposeintroduction.ui.screens.CatalogBrowser. - Chame a função 
itempara adicionar um item à função combinávelLazyColumn. - Declare 
featuredMovieListcomo uma propriedade delegada na lambda transmitida à funçãoiteme defina o objetoStatea ser delegado, que é coletado do atributocatalogBrowserViewModel.featuredMovieList. - Chame a função combinável 
Carouseldentro da funçãoiteme transmita estes parâmetros: 
- O tamanho da variável 
featuredMovieListpor um parâmetroslideCount. - Um objeto 
Modifierpara especificar o tamanho do carrossel com os métodosModifier.fillMaxWidtheModifier.height. A função combinávelCarouselusa 376 dp de altura, transmitindo um valor376.dpao métodoModifier.height. - Uma lambda chamada com um valor inteiro que indica o índice do item do carrossel visível.
 
- Extraia o objeto 
Movieda variávelfeaturedMovieListe do valor de índice fornecido. - Adicione uma função combinável 
Boxà função combinávelCarousel. - Adicione uma função combinável 
Textà função combinávelBoxpara mostrar o título do filme. 
CatalogBrowser.kt
@OptIn(ExperimentalTvMaterial3Api::class)
@Composable
fun CatalogBrowser(
    modifier: Modifier = Modifier,
    catalogBrowserViewModel: CatalogBrowserViewModel = hiltViewModel(),
    onMovieSelected: (Movie) -> Unit = {}
) {
    val categoryList by
    catalogBrowserViewModel.categoryList.collectAsStateWithLifecycle()
    LazyColumn(
        modifier = modifier,
        verticalArrangement = Arrangement.spacedBy(16.dp),
        contentPadding = PaddingValues(horizontal = 48.dp, vertical = 32.dp)
    ) {
        item {
            val featuredMovieList by catalogBrowserViewModel.featuredMovieList.collectAsStateWithLifecycle()
            Carousel(
                slideCount = featuredMovieList.size,
                modifier = Modifier
                    .fillMaxWidth()
                    .height(376.dp)
            ) { indexOfCarouselSlide ->
                val featuredMovie =
                    featuredMovieList[indexOfCarouselSlide]
                Box {
                    Text(text = featuredMovie.title)
                }
            }
        }
        items(categoryList) { category ->
            Text(text = category.name)
            LazyRow(
                horizontalArrangement = Arrangement.spacedBy(8.dp)
            ) {
                items(category.movieList) { movie ->
                    MovieCard(movie = movie, onClick = { onMovieSelected(movie) })
                }
            }
        }
    }
}
Mostrar imagens de plano de fundo
A função combinável Box coloca um componente em cima de outro. Consulte Conceitos básicos de layout para mais detalhes.
Para mostrar imagens de plano de fundo, siga estas etapas:
- Chame a função combinável 
AsyncImagepara carregar a imagem de plano de fundo associada ao objetoMovieantes da função combinávelText. - Atualize a posição e o estilo do texto da função combinável 
Textpara melhorar a visibilidade. - Defina um marcador de posição para a função combinável 
AsyncImagecom o objetivo de evitar a mudança de layout. O código inicial tem um marcador de posição como um drawable que pode ser referenciado com oR.drawable.placeholder. 
CatalogBrowser.kt
@OptIn(ExperimentalTvMaterial3Api::class)
@Composable
fun CatalogBrowser(
    modifier: Modifier = Modifier,
    catalogBrowserViewModel: CatalogBrowserViewModel = hiltViewModel(),
    onMovieSelected: (Movie) -> Unit = {}
) {
    val categoryList by
    catalogBrowserViewModel.categoryList.collectAsStateWithLifecycle()
    LazyColumn(
        modifier = modifier,
        verticalArrangement = Arrangement.spacedBy(16.dp),
        contentPadding = PaddingValues(horizontal = 48.dp, vertical = 32.dp)
    ) {
        item {
            val featuredMovieList by catalogBrowserViewModel.featuredMovieList.collectAsStateWithLifecycle()
            Carousel(
                slideCount = featuredMovieList.size,
                modifier = Modifier
                    .fillMaxWidth()
                    .height(376.dp),
            ) { indexOfCarouselItem ->
                val featuredMovie = featuredMovieList[indexOfCarouselItem]
                Box{
                    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)
            LazyRow(
                horizontalArrangement = Arrangement.spacedBy(8.dp)
            ) {
                items(category.movieList) { movie ->
                    MovieCard(movie = movie, onClick = { onMovieSelected(movie) })
                }
            }
        }
    }
}
Adicionar uma transição de telas à tela de detalhes
É possível adicionar um Button ao carrossel para que os usuários possam acionar uma transição para a tela de detalhes clicando no botão.
Para que os usuários possam conferir os detalhes do filme no carrossel visível na tela de detalhes, siga estas etapas:
- Chame a função combinável 
Columnno elemento combinávelBoxno combinávelCarousel - Mova o elemento combinável 
TextnoCarouselpara a função combinávelColumn. - Chame a função combinável 
Buttondepois da funçãoTextna funçãoColumn. - Chame a função combinável 
Textna funçãoButtoncom o valor de retorno da funçãostringResource, chamada comR.string.show_details. - Chame a função 
onMovieSelectedcom a variávelfeaturedMoviena lambda transmitida ao parâmetroonClickda função combinávelButton. 
CatalogBrowser.kt
@OptIn(ExperimentalTvMaterial3Api::class)
@Composable
fun CatalogBrowser(
    modifier: Modifier = Modifier,
    catalogBrowserViewModel: CatalogBrowserViewModel = hiltViewModel(),
    onMovieSelected: (Movie) -> Unit = {}
) {
    val categoryList by
    catalogBrowserViewModel.categoryList.collectAsStateWithLifecycle()
    LazyColumn(
        modifier = modifier,
        verticalArrangement = Arrangement.spacedBy(16.dp),
        contentPadding = PaddingValues(horizontal = 48.dp, vertical = 32.dp)
    ) {
        item {
            val featuredMovieList by catalogBrowserViewModel.featuredMovieList.collectAsStateWithLifecycle()
            Carousel(
                slideCount = featuredMovieList.size,
                modifier = Modifier
                    .fillMaxWidth()
                    .height(376.dp),
            ) { indexOfCarouselItem ->
                val featuredMovie = featuredMovieList[indexOfCarouselItem]
                Box {
                    AsyncImage(
                        model = featuredMovie.backgroundImageUrl,
                        contentDescription = null,
                        placeholder = painterResource(
                            id = R.drawable.placeholder
                        ),
                        contentScale = ContentScale.Crop,
                        modifier = Modifier.fillMaxSize(),
                    )
                    Column {
                        Text(text = featuredMovie.title)
                        Button(onClick = { onMovieSelected(featuredMovie) }) {
                            Text(text = stringResource(id = R.string.show_details))
                        }
                    }
                }
            }
        }
        items(categoryList) { category ->
            Text(text = category.name)
            LazyRow(
                horizontalArrangement = Arrangement.spacedBy(8.dp)
            ) {
                items(category.movieList) { movie ->
                    MovieCard(movie = movie, onClick = { onMovieSelected(movie) })
                }
            }
        }
    }
}
Opcional: ajustar o layout
Para ajustar o layout do carrossel, siga estas etapas:
- Atribua o valor 
backgroundColorcom o valorMaterialTheme.colorScheme.backgroundna função combinávelCarousel. - Unir a função combinável 
Columna um elemento combinávelBox - Transmita o valor 
Alignment.BottomStartao parâmetrocontentAlignmentdo componenteBox. - Transmita o modificador 
fillMaxSizeao parâmetro modificador da função combinávelBox. O modificadorfillMaxSizeé criado com a funçãoModifier.fillMaxSize(). - Chame o método 
drawBehind()no modificadorfillMaxSizetransmitido ao elemento combinávelBox. - Na lambda transmitida ao modificador 
drawBehind, atribua o valorbrushcom um objetoBrushque é criado ao chamar a funçãoBrush.linearGradientcom uma lista de dois objetosColor. A lista é criada chamando a funçãolistOfcom os valoresbackgroundColoreColor.Transparent. - Chame 
drawRectcom o objetobrushna lambda transmitida ao modificadordrawBehindpara criar uma camada srim sobre a imagem de plano de fundo - Especifique o padding da função combinável 
Columncom o modificadorpadding, que é criado chamandoModifier.paddingcom o valor20.dp. - Adicione uma função combinável 
Spacercom o valor20.dpentre os elementos combináveisTexteButtonna função combinávelColumn. 
CatalogBrowser.kt
@OptIn(ExperimentalTvMaterial3Api::class)
@Composable
fun CatalogBrowser(
    modifier: Modifier = Modifier,
    catalogBrowserViewModel: CatalogBrowserViewModel = hiltViewModel(),
    onMovieSelected: (Movie) -> Unit = {}
) {
    val categoryList by catalogBrowserViewModel.categoryList.collectAsStateWithLifecycle()
    LazyColumn(
        modifier = modifier,
        verticalArrangement = Arrangement.spacedBy(32.dp),
        contentPadding = PaddingValues(horizontal = 58.dp, vertical = 36.dp)
    ) {
        item {
            val featuredMovieList by
            catalogBrowserViewModel.featuredMovieList.collectAsStateWithLifecycle()
            Carousel(
                itemCount = featuredMovieList.size,
                modifier = Modifier
                    .fillMaxWidth()
                    .height(376.dp),
            ) { indexOfCarouselItem ->
                val featuredMovie = featuredMovieList[indexOfCarouselItem]
                val backgroundColor = MaterialTheme.colorScheme.background
                
                Box {
                    AsyncImage(
                        model = featuredMovie.backgroundImageUrl,
                        contentDescription = null,
                        placeholder = painterResource(
                            id = R.drawable.placeholder
                        ),
                        contentScale = ContentScale.Crop,
                        modifier = Modifier.fillMaxSize(),
                    )
                    Box(
                        contentAlignment = Alignment.BottomStart,
                        modifier = Modifier
                            .fillMaxSize()
                            .drawBehind {
                                val brush = Brush.horizontalGradient(
                                    listOf(backgroundColor, Color.Transparent)
                                )
                                drawRect(brush)
                            }
                    ) {
                        Column(
                            modifier = Modifier.padding(20.dp)
                        ) {
                            Text(
                                text = featuredMovie.title,
                                style = MaterialTheme.typography.displaySmall
                            )
                            Spacer(modifier = Modifier.height(28.dp))
                            Button(onClick = { onMovieSelected(featuredMovie) }) {
                                Text(text = stringResource(id = R.string.show_details))
                            }
                        }
                    }
                }
            }
        }
        items(categoryList) { category ->
            Text(text = category.name)
            LazyRow(
                horizontalArrangement = Arrangement.spacedBy(16.dp),
                modifier = Modifier.height(200.dp)
            ) {
                items(category.movieList) { movie ->
                    MovieCard(
                        movie,
                        onClick = {
                            onMovieSelected(it)
                        }
                    )
                }
            }
        }
    }
}
7. Acessar o código da solução
Para fazer o download do código da solução para este codelab, realize uma destas ações:
- Clique no botão a seguir para fazer o download como um arquivo ZIP. Em seguida, descompacte e abra esse arquivo no Android Studio.
 
- Recupere-o com o Git:
 
$ git clone https://github.com/android/tv-codelabs.git $ cd tv-codelabs $ git checkout solution $ cd IntroductionToComposeForTV
- Confira no GitHub (link em inglês).
 
8. Parabéns!
Parabéns! Você aprendeu as noções básicas do Compose para TV:
- Como implementar uma tela para mostrar uma lista de conteúdo combinando LazyColumn e LazyLow.
 - A implementação básica de tela para mostrar detalhes do conteúdo.
 - Como adicionar transições entre as duas telas.