As diretrizes de design do app de TV definem uma variante de card
chamada Card clássico amplo,
que mostra uma imagem e o bloco de conteúdo descritivo lado a lado.
Ao criar layouts bidimensionais complexos para telas de TV,
o uso de posicionamento tradicional baseado em coordenadas
ou contêineres Row e Column profundamente aninhados pode dificultar
a leitura, a manutenção e a refatoração do código.
O card clássico largo é um caso de uso ideal para a API Grid do Jetpack Compose,
que ajuda a estruturar elementos lado a lado de forma limpa usando áreas de layout 2D semânticas
definidas pelo usuário.
Práticas recomendadas
Implemente o layout do card com Grid.
O uso do contêiner Grid melhora a legibilidade do código,
desacoplando as dimensões do layout físico do posicionamento do item filho.
Em vez de depender de índices de faixa exatos,
defina áreas semânticas nomeadas com a função area
e coloque elementos combináveis filhos com o modificador gridItem.
Combine o dimensionamento flexível de faixas, como GridTrackSize.Auto
e minmax(0.dp, 1.fr),
com lacunas exatas (gap(row = 8.dp, column = 16.dp))
para organizar componentes de cards complexos lado a lado de forma clara e robusta.
Ingredientes
Grid: um layout de contêiner 2D no Jetpack Compose que organiza elementos combináveis filhos em linhas e colunas com base em uma lambda de configuração.gridItem: uma extensãoModifierque associa um elemento da interface filho a umareaIdespecífico em um layoutGrid.area: uma função DSL de configuração emGridConfigurationScopeque mapeia umareaIdsemântico para períodos ou intervalos específicos de linhas e colunas.Card: um elemento combinável de contêiner do Material Design que agrupa elementos de interface e estilos relacionados.GridTrackSize.MinMax: uma propriedade de dimensionamento de faixa que define limites flexíveis (minemax) para dimensões de linha ou coluna.GridTrackSize.Auto: uma propriedade de dimensionamento de faixa que dimensiona a faixa de linha ou coluna automaticamente para se ajustar ao conteúdo (column(GridTrackSize.Auto)).gap: uma função de configuração que especifica o espaçamento vertical (row) e horizontal (column) entre as faixas da grade.
Etapas
Defina identificadores de área, configure as faixas e áreas da grade e posicione os elementos de composição filhos.
1. Definir identificadores de área semântica
Primeiro, crie uma representação com segurança de tipos das regiões dentro do layout de card
clássico amplo. Uma classe enum evita erros de digitação de strings e oferece
segurança no tempo de compilação ao mapear faixas e posicionar itens filhos.
enum class CardArea { Image, Title, Subtitle, Description, ExtraText, }
2. Configurar as faixas e as áreas nomeadas da grade 2D
No lambda config do elemento combinável Grid, defina as colunas e linhas
que compõem a estrutura do card. Use GridTrackSize.Auto para a coluna de imagem,
para que ela envolva a largura da miniatura de forma limpa. Use minmax(0.dp, 1.fr) para a
coluna de conteúdo para que ela se expanda de forma flexível pela largura restante do card. Especifique
o espaçamento entre faixas com gap(row = 8.dp, column = 16.dp). Mapeie os identificadores de CardArea
para as coordenadas físicas da pista usando a função area.
Grid( config = { // Define columns: left column for image, right for content column(GridTrackSize.Auto) column(minmax(0.dp, 1.fr)) // Define row tracks for the vertical content stack row(GridTrackSize.Auto) row(GridTrackSize.Auto) row(GridTrackSize.Auto) row(GridTrackSize.Auto) // Map semantic identifiers to grid coordinates and spans area(CardArea.Image, row = 1, column = 1, rowSpan = 4) area(CardArea.Title, row = 1, column = 2) area(CardArea.Subtitle, row = 2, column = 2) area(CardArea.Description, row = 3, column = 2) area(CardArea.ExtraText, row = 4, column = 2) gap(row = 8.dp, column = 16.dp) } ) { // Child elements placed in Step 3 }
3. Posicionar elementos combináveis filhos usando o modificador gridItem
Com as faixas e áreas configuradas, posicione cada elemento filho da interface puramente por
intenção semântica usando o modificador gridItem. É possível declarar elementos combináveis
filhos em qualquer ordem. O contêiner Grid posiciona cada elemento
diretamente na região 2D atribuída de forma automática.
@Composable fun WideClassicCard( imageContent: @Composable () -> Unit, title: String, subtitle: String, description: String, extraText: String, modifier: Modifier = Modifier, ) { Card(modifier = modifier) { Grid( config = { // Define columns: left column for image, right for content column(GridTrackSize.Auto) column(minmax(0.dp, 1.fr)) // Define row tracks for the vertical content stack row(GridTrackSize.Auto) row(GridTrackSize.Auto) row(GridTrackSize.Auto) row(GridTrackSize.Auto) // Map semantic identifiers to grid coordinates and spans area(CardArea.Image, row = 1, column = 1, rowSpan = 4) area(CardArea.Title, row = 1, column = 2) area(CardArea.Subtitle, row = 2, column = 2) area(CardArea.Description, row = 3, column = 2) area(CardArea.ExtraText, row = 4, column = 2) gap(row = 8.dp, column = 16.dp) }, ) { Box(modifier = Modifier.gridItem(CardArea.Image)) { imageContent() } Text( text = title, style = MaterialTheme.typography.titleLarge, modifier = Modifier .gridItem(CardArea.Title) .padding(top = 16.dp, end = 16.dp), ) Text( text = subtitle, style = MaterialTheme.typography.titleMedium, modifier = Modifier .gridItem(CardArea.Subtitle) .padding(end = 16.dp), ) Text( text = description, style = MaterialTheme.typography.bodyMedium, modifier = Modifier .gridItem(CardArea.Description) .padding(end = 16.dp), ) Text( text = extraText, style = MaterialTheme.typography.labelSmall, modifier = Modifier .gridItem(CardArea.ExtraText) .padding(bottom = 16.dp, end = 16.dp), ) } } }
Resultados
Ao organizar o card clássico largo com Grid e áreas nomeadas,
você consegue uma separação clara entre as faixas de layout físico, as coordenadas
de área e o posicionamento do elemento filho que pode ser composto.
Essa estrutura torna o layout legível e fácil de manter,
além de seguir as diretrizes de design de apps de TV
para componentes de miniatura e descrição lado a lado.