Como desenhar telas no Jetpack Compose
Tags: android, kotlin, programming, jetpackcompose
Published: 2023-06-14 11:00:00

O [Jetpack Compose](https://developer.android.com/jetpack/compose) é uma ferramenta bastante poderosa para criar telas nativas no Android.

Embora a ideia da ferramenta seja facilitar a implementação de telas, se não compreendermos a finalidade de cada componente, não será uma tarefa fácil!

Considerando esses detalhes, vamos entender como devemos pensar no momento que criamos uma tela.

## Amostra de implementação

Como demonstração, vamos utilizar a seguinte tela:

![Preview de um card com bordas arredondas, imagem no topo preenchendo toda a largura do card, abaixo em layout de coluna, contém o título, subtítulo, ícone de favorito e botão com mensagem 'ler agora'. ícone de favorito e botão estão em layout de linha, sendo que o ícone está na extrema esquerda e botão na extrema direita](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/udrs4yx6ruygl1vabhzi.png)

Agora que conhecemos a tela, precisamos compreender a lógica de analise antes de começar a implementação.

## Identificando os elementos visuais

Como primeiro passo, podemos começar a analise identificando os elementos visuais. De uma forma genérica, temos:

- imagem de capa
- texto de título
- texto de subtítulo
- ícone de favorito
- botão de leitura

## Analisando o layout da tela

Então, precisamos analisar o layout geral. Observe que todos os elementos estão organizados em coluna, sendo que o ícone e botão, estão em linha!

Com apenas essa analise, podemos implementar o primeiro código da tela com [composable de layout](https://developer.android.com/jetpack/compose/layouts/basics#standard-layouts):

```kotlin
Column {
    AsyncImage(
        "https://picsum.photos/1920/1080",
        contentDescription = "Imagem de capa"
    )
    Text(text = "Título")
    Text(text = "Subtitulo")
    Row {
        Icon(
            Icons.Outlined.FavoriteBorder,
            contentDescription = "ícone de favorito"
        )
        Button(onClick = { /*TODO*/ }) {
            Text(text = "Ler agora")
        }
    }
}
```

![App em execução apresentando a tela com imagem, título, subtítulo, ícone e botão no layout de coluna. Ícone e botão no layout em linha](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/d9f4k6l1z2je5weulmjb.png)

Embora o design não esteja fiel a amostra, já temos tudo que precisamos para seguir com o próximo passo, personalizar os componentes da tela.

> Fazendo uma analogia ao front-end na web, basicamente, adicionamos o HTML para estruturar a página!

## Personalizando a tela com o Modifier

Assim como há os composables para estruturar a tela, temos o `Modifier` como entidade responsável em personalizar, dar comportamentos e reagir a eventos dos composables. Sendo assim, vamos utilizá-lo para modificar os composables

> Seguindo a analogia com front-end, o `Modifier` seria um CSS do Jetpack Compose.

## Ajustando o container principal

Podemos começar a personalização por qualquer componente, mas, geralmente tendo seguir uma estratégia, começar pelo componente mais externo até o mais interno, portanto, vamos começar com o `Column` mais externo:

```kotlin
Column(
    Modifier
        .padding(8.dp)
        .fillMaxWidth()
        .border(
            0.5.dp,
            Color.Gray.copy(alpha = 0.5f),
            RoundedCornerShape(20.dp)
        )
) {
    ...
}
```

![App em execução apresentado o card com espaçamento entre as extremidades da tela e bordas arredondadas](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/67b2ubrv5pqtcagmaval.png)

> Todo composable recebe como primeiro parâmetro opcional um `Modifier`, pois existe o `Modifier` padrão. Para mais detalhes, você pode [verificar nesta thread sobre modificadores.](https://twitter.com/alexfelipi/status/1660615743375917056).

Note que agora já temos um destaque ao container que representa o card, exibindo um espaçamento e bordas arredondadas.

## Personalizando a imagem de capa

Por mais que o container do card foi ajustado corretamente, a imagem ainda apresenta o aspecto quadrado e ultrapassa as extremidades do container principal, para resolver isso, podemos aplicar o modificador na imagem também:

```kotlin
Column(
    ...
) {
    AsyncImage(
        "https://picsum.photos/1920/1080",
        contentDescription = "Imagem de capa",
        Modifier
            .height(100.dp)
            .clip(
                RoundedCornerShape(
                    topStart = 20.dp,
                    topEnd = 20.dp
                )
            ),
        contentScale = ContentScale.Crop,
        placeholder = ColorPainter(Color.Gray)
    )
    ...
}
```

![Card e imagem de capa com as bordas arredondadas](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/skzu1n6rema7k0de8mrc.png)

Note que além do modificador, utilizei alguns parâmetros próprios do `AsyncImage`: 

- `contentScale`: o container da imagem vai ter uma altura fixa, na escala padrão, ela fica distorcida ao tentar exibir todo conteúdo da imagem. Aplicando o corte com o `ContentScale.Crop`, apresenta a imagem cortada no espaço disponível.
- `placeholder`: apresenta conteúdo no preview e durante o carregamento da imagem via internet, nesse caso, um fundo cinza

Agora que a imagem de capa foi ajustada, vamos para os textos.

## Modificando os textos

Podemos modificar os textos da seguinte maneira:

```kotlin
Column(
    ...
) {
    ...
    Text(
        text = "Título",
        Modifier.padding(
            start = 16.dp,
            top = 16.dp,
            end = 16.dp,
            bottom = 8.dp
        ),
        fontSize = 24.sp
    )
    Text(
        text = "Subtitulo",
        Modifier.padding(
            start = 16.dp,
            end = 16.dp,
            bottom = 16.dp
        ),
        fontSize =
        14.sp
    )
    ...
}

}
```

![Card aplicando espaçamento e tamanho de fonte no título e subtitulo](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/n9ksu01lsaf0lmp4p25x.png)

Mesmo que os textos foram modificados como esperado, é válido notar que tivemos que aplicar diversos números para o espaçamento... 

Em casos como esses, podemos utilizar composables de layout para simplificar a implementação, como por exemplo, envolver os `Text`s em coluna e modificar o espaçamento da coluna e ajustar o arranjo vertical:

```kotlin
Column(
    ...
) {
    ...
    Column(
        Modifier.padding(16.dp),
        verticalArrangement = Arrangement.spacedBy(8.dp)
    ) {
        Text(
            text = "Título",
            fontSize = 24.sp
        )
        Text(
            text = "Subtitulo",
            fontSize = 14.sp
        )
    }
    ...
}
```

> Tanto o `Row` como o `Column`, possuem parâmetros extras para manipular os seus filhos, ou seja, os composables que são organizados por eles.

Com esse ajuste, temos o mesmo resultado de antes! Mais simples, concorda? Por fim, precisamos apenas ajustar a linha final.

## Ajustando o ícone e botão em linha

O ajuste da linha é bastante similar ao que fizemos na coluna anterior:

```kotlin
Column(
    ...
) {
    ...
    Row(
        Modifier
            .padding(16.dp)
            .fillMaxWidth(),
        horizontalArrangement = Arrangement.SpaceBetween,
        verticalAlignment = Alignment.CenterVertically
    ) {
        Icon(
            Icons.Outlined.FavoriteBorder,
            contentDescription = "ícone de favorito"
        )
        Button(onClick = { /*TODO*/ }) {
            Text(text = "Ler agora")
        }
    }
}
```

![Card com o ícone de favorito e botão para ler em linha alinhados nas extremidades da tela, sendo o ícone a esquerda e botão direito na direita. Também, com espaçamento aplicado](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zbtyjoaroofnoc3n17kn.png)

A grande diferença fica na aplicação da largura com o `fillMaxWidth()` em conjunto do arranjo e alinhamento.

Essa técnica é utilizada para que seja possível aplicar o `Arrangement.SpaceBetween` que organiza os filhos nas extremidades, já o alinhamento, centraliza os filhos verticalmente.

## Conclusão

Em um primeiro momento, a implementação de telas no Jetpack Compose pode parecer complexa, porém, vimos que podemos aplicar algumas técnicas para facilitar a escrita de código.

Considerando o que vimos, temos:

- identificação dos elementos visuais
- analise do layout 
- aplicação de modificadores em cada componente
- ordem de modificação para facilitar

O que achou de criar telas no Jetpack Compose? Você já usa essas técnicas ou tem outros macetes para facilitar a implementação?