Jetpack Compose: UI declarativa para Android
O Jetpack Compose é a nova toolkit do Google para construir UIs nativas no Android. Diferente do sistema tradicional baseado em XML, o Compose usa funções Kotlin para descrever a interface. É mais simples, mais expressivo, e eliminou toneladas de boilerplate.
Por que Compose?
O Android tradicional exigia:
- XML para layout
- Java/Kotlin para lógica
findViewByIdpara conectar os dois- Adapters para listas
- Fragments para telas
O Compose unifica tudo em Kotlin puro:
// Isso é TUDO que você precisa para um botão
Button(onClick = { /* ação */ }) {
Text("Clique aqui")
}
Componentes básicos (Composable functions)
No Compose, tudo é uma função @Composable:
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
@Composable
fun Saudacao(nome: String) {
Text(text = "Olá, $nome!")
}
Layouts
import androidx.compose.foundation.layout.*
import androidx.compose.material3.*
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
@Composable
fun MeuLayout() {
Column(
modifier = Modifier
.fillMaxSize()
.padding(16.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
Text(
text = "Bem-vindo!",
style = MaterialTheme.typography.headlineLarge
)
Spacer(modifier = Modifier.height(16.dp))
Text(
text = "Escolha uma opção:",
style = MaterialTheme.typography.bodyLarge
)
Spacer(modifier = Modifier.height(24.dp))
Row(
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
Button(onClick = { /* ação */ }) {
Text("Opção 1")
}
OutlinedButton(onClick = { /* ação */ }) {
Text("Opção 2")
}
}
}
}
Estado (State)
O Compose re-renderiza automaticamente quando o estado muda:
import androidx.compose.runtime.*
@Composable
fun Contador() {
var contador by remember { mutableStateOf(0) }
Column(horizontalAlignment = Alignment.CenterHorizontally) {
Text(
text = "Contador: $contador",
style = MaterialTheme.typography.headlineMedium
)
Spacer(modifier = Modifier.height(16.dp))
Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
Button(onClick = { contador-- }) {
Text("-")
}
Button(onClick = { contador++ }) {
Text("+")
}
}
}
}
remember mantém o estado entre re-renderizações. Sem ele, o contador resetaria toda vez.
Lista de tarefas completa
data class Tarefa(
val id: Int,
val titulo: String,
val concluida: Boolean = false
)
@Composable
fun ListaTarefas() {
var tarefas by remember { mutableStateOf(listOf<Tarefa>()) }
var texto by remember { mutableStateOf("") }
Column(modifier = Modifier.padding(16.dp)) {
// Input
Row(verticalAlignment = Alignment.CenterVertically) {
OutlinedTextField(
value = texto,
onValueChange = { texto = it },
label = { Text("Nova tarefa") },
modifier = Modifier.weight(1f)
)
Spacer(modifier = Modifier.width(8.dp))
Button(onClick = {
if (texto.isNotBlank()) {
tarefas = tarefas + Tarefa(
id = tarefas.size + 1,
titulo = texto
)
texto = ""
}
}) {
Text("Add")
}
}
Spacer(modifier = Modifier.height(16.dp))
// Lista
LazyColumn {
items(tarefas) { tarefa ->
Card(
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 4.dp)
) {
Row(
modifier = Modifier.padding(16.dp),
verticalAlignment = Alignment.CenterVertically
) {
Checkbox(
checked = tarefa.concluida,
onCheckedChange = { checked ->
tarefas = tarefas.map {
if (it.id == tarefa.id)
it.copy(concluida = checked)
else it
}
}
)
Text(
text = tarefa.titulo,
modifier = Modifier.weight(1f)
)
IconButton(onClick = {
tarefas = tarefas.filter { it.id != tarefa.id }
}) {
Icon(
Icons.Default.Delete,
contentDescription = "Remover"
)
}
}
}
}
}
}
}
Navegação
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
@Composable
fun MeuApp() {
val navController = rememberNavController()
NavHost(navController, startDestination = "home") {
composable("home") {
TelaHome(
onNavigateToDetalhes = { id ->
navController.navigate("detalhes/$id")
}
)
}
composable("detalhes/{id}") { backStackEntry ->
val id = backStackEntry.arguments?.getString("id")
TelaDetalhes(id = id)
}
}
}
Theme
import androidx.compose.material3.*
private val DarkColorScheme = darkColorScheme(
primary = Color(0xFF58A6FF),
secondary = Color(0xFF8B949E),
background = Color(0xFF0D1117),
surface = Color(0xFF161B22),
)
@Composable
fun MeuTema(content: @Composable () -> Unit) {
MaterialTheme(
colorScheme = DarkColorScheme,
typography = Typography(),
content = content
)
}
XML vs Compose
| Aspecto | XML + Views | Compose |
|---|---|---|
| Boilerplate | Alto | Mínimo |
| Estado | Manual (LiveData, StateFlow) | Automático (State) |
| Preview | Precisa rodar o app | Preview no Android Studio |
| Animações | Complexas | Simples e declarativas |
| Learning curve | Baixa (maduro) | Média (conceitos novos) |
| Performance | Excelente | Excelente (equivalente) |
Conclusão
Jetpack Compose é o futuro do desenvolvimento Android. A Google está investindo pesado — todas as novas APIs são Compose-first, e o XML está em manutenção. Se você está começando em Android agora, comece direto com Compose.
A curva de aprendizado é suave se você já conhece Kotlin. O conceito de “UI como função” é intuitivo depois que você entende o ciclo de recomposição.
Se você vem de React ou Flutter, vai se sentir em casa — o modelo mental é muito similar.
Caso eu tenha falado alguma besteira, por favor, agradecerei correções e sugestões.