O que e Modifier em Kotlin?

Modifier e o mecanismo do Jetpack Compose para decorar, configurar e modificar componentes de UI. Ele controla aparencia (tamanho, padding, cor de fundo), comportamento (clique, scroll, foco) e layout (alinhamento, preenchimento, posicionamento) de qualquer Composable.

Em vez de definir atributos em XML como no sistema antigo de Views, voce encadeia chamadas de Modifier em uma sintaxe fluente e expressiva. A ordem das chamadas importa e afeta o resultado visual.

Sintaxe basica

import androidx.compose.foundation.layout.*
import androidx.compose.material3.*
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp

@Composable
fun CartaoSimples() {
    Card(
        modifier = Modifier
            .fillMaxWidth()
            .padding(16.dp)
    ) {
        Text(
            text = "Conteudo do cartao",
            modifier = Modifier.padding(24.dp)
        )
    }
}

O Modifier comeca como um objeto companheiro (singleton) e cada chamada retorna um novo Modifier com a modificacao aplicada. A cadeia e imutavel: cada passo cria um novo objeto.

A ordem importa

A ordem em que voce encadeia modifiers afeta diretamente o resultado:

// Padding ANTES do background: espaco transparente ao redor do fundo
@Composable
fun ExemploA() {
    Box(
        modifier = Modifier
            .padding(16.dp)
            .background(Color.Blue)
            .size(100.dp)
    )
}

// Background ANTES do padding: fundo preenche o espaco do padding
@Composable
fun ExemploB() {
    Box(
        modifier = Modifier
            .background(Color.Blue)
            .padding(16.dp)
            .size(100.dp)
    )
}

Pense nos modifiers como camadas aplicadas de fora para dentro. O primeiro modifier na cadeia e a camada mais externa.

Modifiers de tamanho

@Composable
fun ExemplosTamanho() {
    // Tamanho fixo
    Box(modifier = Modifier.size(100.dp))

    // Largura e altura separadas
    Box(modifier = Modifier.width(200.dp).height(50.dp))

    // Preencher todo o espaco disponivel
    Box(modifier = Modifier.fillMaxSize())

    // Preencher apenas largura
    Box(modifier = Modifier.fillMaxWidth())

    // Preencher uma fracao
    Box(modifier = Modifier.fillMaxWidth(0.5f)) // 50% da largura

    // Tamanho minimo e maximo
    Box(modifier = Modifier.widthIn(min = 100.dp, max = 300.dp))

    // Tamanho baseado em aspecto
    Box(modifier = Modifier.aspectRatio(16f / 9f))
}

Modifiers de aparencia

@Composable
fun ExemplosAparencia() {
    Box(
        modifier = Modifier
            .size(200.dp)
            .background(
                color = Color.Blue,
                shape = RoundedCornerShape(16.dp)
            )
            .border(
                width = 2.dp,
                color = Color.DarkGray,
                shape = RoundedCornerShape(16.dp)
            )
            .shadow(
                elevation = 8.dp,
                shape = RoundedCornerShape(16.dp)
            )
            .alpha(0.9f)
    )
}

Modifiers de interacao

@Composable
fun ExemplosInteracao() {
    var clicado by remember { mutableStateOf(false) }

    Box(
        modifier = Modifier
            .clickable { clicado = !clicado }
            .combinedClickable(
                onClick = { /* clique simples */ },
                onLongClick = { /* clique longo */ },
                onDoubleClick = { /* clique duplo */ }
            )
    )

    // Scroll
    Column(
        modifier = Modifier.verticalScroll(rememberScrollState())
    ) {
        // Conteudo scrollavel
    }

    // Arrastar
    Box(
        modifier = Modifier.draggable(
            orientation = Orientation.Horizontal,
            state = rememberDraggableState { delta ->
                // Processar delta do arrasto
            }
        )
    )
}

Criando Modifiers customizados

Voce pode criar extension functions em Modifier para reutilizar combinacoes:

fun Modifier.cartaoPadrao(): Modifier = this
    .fillMaxWidth()
    .padding(horizontal = 16.dp, vertical = 8.dp)
    .shadow(4.dp, RoundedCornerShape(12.dp))
    .background(Color.White, RoundedCornerShape(12.dp))
    .padding(16.dp)

@Composable
fun MeuCartao(titulo: String) {
    Column(modifier = Modifier.cartaoPadrao()) {
        Text(text = titulo)
    }
}

Para modifiers que dependem de estado do Compose, use composed:

fun Modifier.shimmerEffect(): Modifier = composed {
    val transition = rememberInfiniteTransition()
    val alpha by transition.animateFloat(
        initialValue = 0.2f,
        targetValue = 1f,
        animationSpec = infiniteRepeatable(
            animation = tween(1000),
            repeatMode = RepeatMode.Reverse
        )
    )
    this.alpha(alpha)
}

Modifier como parametro

O padrao recomendado e aceitar Modifier como parametro em Composables reutilizaveis:

@Composable
fun BotaoCustom(
    texto: String,
    onClick: () -> Unit,
    modifier: Modifier = Modifier // Valor padrao: Modifier vazio
) {
    Button(
        onClick = onClick,
        modifier = modifier
            .height(48.dp)
            .fillMaxWidth()
    ) {
        Text(text = texto)
    }
}

// Uso: o chamador pode adicionar modifiers extras
@Composable
fun Tela() {
    BotaoCustom(
        texto = "Salvar",
        onClick = { /* ... */ },
        modifier = Modifier.padding(16.dp) // Adicionado pelo chamador
    )
}

O parametro modifier deve ser o primeiro parametro opcional e ter Modifier (vazio) como valor padrao. Isso segue as convencoes da API do Compose.

Modifier.then para composicao condicional

@Composable
fun ComponenteCondicional(habilitado: Boolean) {
    val modifierBase = Modifier.fillMaxWidth().padding(16.dp)

    val modifierFinal = if (habilitado) {
        modifierBase.then(Modifier.clickable { /* acao */ })
    } else {
        modifierBase.then(Modifier.alpha(0.5f))
    }

    Box(modifier = modifierFinal) {
        Text("Conteudo")
    }
}

Quando usar Modifier

  • Sempre: praticamente todo Composable aceita e deveria aceitar um parametro Modifier. E a forma padrao de configurar aparencia e comportamento.
  • Componentes reutilizaveis: sempre aceite modifier como parametro para permitir customizacao pelo chamador.
  • Estilizacao consistente: crie extension functions de Modifier para padroes visuais reutilizaveis.
  • Layout: Modifiers controlam como componentes se posicionam e ocupam espaco.

Erros comuns

  1. Ignorar a ordem dos modifiers: padding antes de background e diferente de background antes de padding. Sempre teste visualmente com @Preview.

  2. Nao aceitar Modifier como parametro: componentes que nao aceitam Modifier nao podem ser estilizados externamente, reduzindo a reutilizacao.

  3. Usar Modifier.size fixo em excesso: tamanhos fixos nao se adaptam a diferentes telas. Prefira fillMaxWidth com padding ou widthIn com limites.

  4. Criar modifiers dentro de lambdas de recomposicao: se um modifier e criado com composed ou remember, certifique-se de que nao esta sendo recriado desnecessariamente.

  5. Nao encadear com o modifier recebido: quando voce recebe modifier como parametro, aplique-o no elemento raiz antes de adicionar seus proprios modifiers.

Termos relacionados

  • Composable: funcao anotada com @Composable que descreve UI e aceita Modifiers.
  • State: estado reativo que pode ser usado em conjunto com modifiers condicionais.
  • Layout: sistema de posicionamento do Compose onde Modifiers influenciam tamanho e posicao.
  • padding/size/fillMaxWidth: funcoes de extensao de Modifier para configurar dimensoes.
  • clickable: modifier de interacao que torna componentes clicaveis.
  • Recomposicao: processo que reaplica modifiers quando o estado muda.

Modifier e o canivete suico do Jetpack Compose. Dominar a encadeamento de modifiers, entender a importancia da ordem e criar modifiers customizados reutilizaveis sao habilidades fundamentais para construir interfaces bonitas e funcionais em Kotlin.