💻 Exemplos: Capítulo 04: Gerenciamento de Estado no Jetpack Compose
Exemplos de Código: Capítulo 04 — Gerenciamento de Estado no Compose
Especialização em Mobile Nativo com Android • Demonstrações Práticas Compiláveis
💻 Código de Demonstração: State Hoisting, rememberSaveable e Unidirectional Data Flow (UDF)
O código abaixo demonstra a gestão idiomática de estado no Jetpack Compose:
- Diferenciação clara entre Stateful Composable (gerencia o estado) e Stateless Composable (puramente orientado a renderização e eventos).
- Sobrevivência à rotação de tela e recriação de Activity usando
rememberSaveable. - Padrão arquitetural Unidirectional Data Flow (UDF): o estado desce (State Flows Down), os eventos sobem (Events Flow Up).
package br.com.portal.mobile.android.cap04
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Add
import androidx.compose.material.icons.filled.Clear
import androidx.compose.material.icons.filled.Remove
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.ElevatedCard
import androidx.compose.material3.FilledTonalButton
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.LinearProgressIndicator
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
// =====================================================================
// 1. Stateful Composable (Responsável por reter e persistir o estado)
// =====================================================================
@Composable
fun MetaEconomiaContainer(modifier: Modifier = Modifier) {
// rememberSaveable sobrevive à rotação de tela e morte do processo pelo SO
var saldoPoupado by rememberSaveable { mutableIntStateOf(150) }
val metaTotal = 1000
MetaEconomiaContent(
saldoAtual = saldoPoupado,
metaTotal = metaTotal,
onDepositar = { valor -> saldoPoupado = (saldoPoupado + valor).coerceAtMost(metaTotal) },
onRetirar = { valor -> saldoPoupado = (saldoPoupado - valor).coerceAtLeast(0) },
onResetar = { saldoPoupado = 0 },
modifier = modifier
)
}
// =====================================================================
// 2. Stateless Composable (Reutilizável, Testável e Desacoplado)
// =====================================================================
@Composable
fun MetaEconomiaContent(
saldoAtual: Int,
metaTotal: Int,
onDepositar: (Int) -> Unit,
onRetirar: (Int) -> Unit,
onResetar: () -> Unit,
modifier: Modifier = Modifier
) {
val progresso = (saldoAtual.toFloat() / metaTotal.toFloat()).coerceIn(0f, 1f)
ElevatedCard(
shape = RoundedCornerShape(20.dp),
colors = CardDefaults.elevatedCardColors(containerColor = MaterialTheme.colorScheme.surfaceVariant),
modifier = modifier
.fillMaxWidth()
.padding(16.dp)
) {
Column(
modifier = Modifier
.fillMaxWidth()
.padding(20.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Text(
text = "Meta Financeira: Reserva",
style = MaterialTheme.typography.titleMedium,
fontWeight = FontWeight.Bold
)
IconButton(onClick = onResetar) {
Icon(imageVector = Icons.Default.Clear, contentDescription = "Zerar Meta")
}
}
Spacer(modifier = Modifier.height(16.dp))
// Display do Saldo
Text(
text = "R$ $saldoAtual,00",
style = MaterialTheme.typography.headlineLarge,
fontWeight = FontWeight.ExtraBold,
color = MaterialTheme.colorScheme.primary
)
Text(
text = "Objetivo total: R$ $metaTotal,00 (${(progresso * 100).toInt()}%)",
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
Spacer(modifier = Modifier.height(16.dp))
// Barra de Progresso Reativa
LinearProgressIndicator(
progress = { progresso },
modifier = Modifier
.fillMaxWidth()
.height(10.dp),
color = MaterialTheme.colorScheme.primary,
trackColor = MaterialTheme.colorScheme.surface
)
Spacer(modifier = Modifier.height(24.dp))
// Ações do Usuário (Eventos disparam callbacks para cima)
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceEvenly
) {
OutlinedButton(
onClick = { onRetirar(50) },
enabled = saldoAtual > 0
) {
Icon(imageVector = Icons.Default.Remove, contentDescription = null)
Spacer(modifier = Modifier.width(4.dp))
Text(text = "R$ 50")
}
FilledTonalButton(
onClick = { onDepositar(50) },
enabled = saldoAtual < metaTotal
) {
Icon(imageVector = Icons.Default.Add, contentDescription = null)
Spacer(modifier = Modifier.width(4.dp))
Text(text = "R$ 50")
}
}
}
}
}
// =====================================================================
// 3. Preview Interativo
// =====================================================================
@Preview(showBackground = true)
@Composable
fun MetaEconomiaPreview() {
MaterialTheme {
MetaEconomiaContainer()
}
}