Pular para conteúdo

Aula 17 - Jetpack Compose Avançado e Design System Mobile 📱

Objetivo Pedagógico

Objetivo: Projetar interfaces declarativas de alta performance com Jetpack Compose: gerenciamento de recomposição, estado unidirecional (UDF), Custom Layouts, animações de transição e criação de um Design System corporativo coeso.


📑 1. Fundamentos Teóricos & Análise Técnica

O desenvolvimento de interfaces gráficas para Android passou por uma revolução com o abandono do paradigma imperativo baseado em Views XML em favor do Jetpack Compose, um toolkit declarativo e reativo nativo.

O domínio avançado do Compose exige a compreensão profunda do seu compilador e runtime: 1. O Modelo Mental da Recomposição: - Uma função @Composable é transformada pelo plugin do compilador do Kotlin em uma árvore de nós em memória. - Recomposição Inteligente: O runtime do Compose rastreia quais estados foram lidos durante a execução de cada função composable. Quando um estado sofre mutação, apenas os composables que leem diretamente aquele estado são reexecutados. Composables cujos parâmetros não sofreram alteração são ignorados (Skipping). - Estabilidade de Tipos (@Stable e @Immutable): Se uma classe possui propriedades mutáveis sem garantia de notificação, o compilador a marca como instável, desabilitando o skipping e forçando recomposições desnecessárias que afetam a taxa de quadros (60/120 FPS). 2. Unidirectional Data Flow (UDF): - Os eventos fluem exclusivamente para cima (Events Up), disparados pelas interações do usuário nos composables em direção ao ViewModel. - O estado consolidado flui exclusivamente para baixo (State Down), imutável e centralizado, eliminando condições de corrida na UI. 3. Design System Corporativo com Material 3: - Estruturação de tokens de design: paleta tonal dinâmica baseada no wallpaper do usuário (Dynamic Color do Monet engine), tipografia fluida com escala de tipos e tokens semânticos de elevação e espaçamento.

📐 Arquitetura Conceitual & Diagrama de Fluxo

graph TD
    User["Interação do Usuário (Click / Input)"] -->|Event Up| VM["ViewModel (UDF Controller)"]
    VM -->|Mutação de Estado| State["StateFlow<UiState> (Imutável)"]
    State -->|State Down| Screen["Composable Screen (Root)"]
    Screen -->|Passa Sub-estados| CompA["Componente Recomponível A (Recompõe)"]
    Screen -->|Parâmetros Inalterados| CompB["Componente Recomponível B (SKIPPED!)"]
    style User fill:#e1f5fe,stroke:#01579b
    style VM fill:#fff3e0,stroke:#e65100
    style State fill:#f3e5f5,stroke:#7b1fa2
    style CompA fill:#e8f5e9,stroke:#2e7d32
    style CompB fill:#eeeeee,stroke:#9e9e9e

🔍 Pilares e Diretrizes Técnicas

Nesta unidade, aprofundamos os seguintes conceitos fundamentais: - Estabilidade e Smart Recomposition: Uso de anotações @Immutable em Data Classes e tipagem estável para maximizar o skipping de funções composable. - Elevação de Estado (State Hoisting): Padrão arquitetural que move o estado para o chamador da função para tornar os componentes reutilizáveis e fáceis de testar. - Subcomposition e Layout Customizado: Criação de componentes com medição sob demanda (MeasurePolicy) para posicionamentos geométricos complexos. - Side-Effects Gerenciados: Controle rigoroso de efeitos colaterais com LaunchedEffect, rememberCoroutineScope e DisposableEffect.


🛠️ 2. Implementação Prática em Android Moderno com Jetpack Compose

Abaixo está a implementação técnica de referência, estruturada com padrões de engenharia de software e foco em robustez:

// DesignSystemComponents.kt (Design System com Tokens Material 3 e Estado Estável)
package com.portal.android.designsystem

import androidx.compose.animation.animateColorAsState
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp

@Immutable
data class OrderCardUiState(
    val id: String,
    val title: String,
    val amountFormatted: String,
    val isPriority: Boolean
)

@Composable
fun OrderStatusCard(
    state: OrderCardUiState,
    onCardClicked: (String) -> Unit,
    modifier: Modifier = Modifier
) {
    val backgroundColor by animateColorAsState(
        targetValue = if (state.isPriority) MaterialTheme.colorScheme.errorContainer 
                      else MaterialTheme.colorScheme.surfaceVariant,
        label = "CardBackgroundColorAnimation"
    )

    Card(
        onClick = { onCardClicked(state.id) },
        shape = RoundedCornerShape(16.dp),
        modifier = modifier
            .fillMaxWidth()
            .padding(horizontal = 16.dp, vertical = 8.dp)
            .background(backgroundColor, RoundedCornerShape(16.dp))
    ) {
        Column(modifier = Modifier.padding(16.dp)) {
            Text(
                text = state.title,
                style = MaterialTheme.typography.titleMedium,
                color = MaterialTheme.colorScheme.onSurface
            )
            Spacer(modifier = Modifier.height(4.dp))
            Text(
                text = state.amountFormatted,
                style = MaterialTheme.typography.bodyLarge,
                color = MaterialTheme.colorScheme.primary
            )
        }
    }
}

💡 Análise Passo a Passo do Código

  1. Anotação @Immutable: Informa explicitamente ao compilador do Compose que os campos não mudam após instanciação, permitindo skipping seguro.
  2. Transição Animada de Cores: Utiliza animateColorAsState para transições visuais fluidas a 60 FPS sem travamentos na thread principal.
  3. State Hoisting no Callback: O componente não conhece o ViewModel; apenas emite o evento onCardClicked para o chamador superior.

🎯 3. Próximos Passos & Sequência Didática