Kotlin: Noções Básicas de Android com Kotlin Explicadas

Última atualização: 2026-08-26

O desenvolvimento Android adotou totalmente o Kotlin — Jetpack Compose constrói UI declarativa com DSL Kotlin, ViewModel + StateFlow gerencia estado reativo, e Room + corrotinas simplificam a camada de dados. Charlie constrói o painel de pedidos Android.

1. O que Você Aprenderá


2. A História Real de um Desenvolvedor

(1) Problema: O Inferno da Sincronização de Estado na UI Imperativa

Alice usou XML + RecyclerView para construir a lista de pedidos. Cada mudança de estado exigia atualização manual da UI: quando os dados mudavam, chamar notifyDataSetChanged(); durante carregamento, mostrar/esconder uma barra de progresso — 10 estados precisavam de 20+ linhas de código de sincronização de UI.

(2) Solução de UI Declarativa com Compose

KOTLIN
// XML + RecyclerView: imperativo, propenso a erros
// recyclerView.adapter.notifyDataSetChanged()

// Compose: declarativo, automático
@Composable
fun OrderList(orders: List<Order>) {
    LazyColumn {
        items(orders) { order ->
            OrderCard(order)  // UI reflete automaticamente mudanças nos dados
        }
    }
}

Compose é declarativo — UI é uma função do estado. Quando os dados mudam, a UI atualiza automaticamente sem sincronização manual.


3. Ciclo de Vida de Activity

(1) Callbacks do Ciclo de Vida

KOTLIN
class OrderActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            OrderScreen()
        }
    }

    override fun onResume() {
        super.onResume()
        // Atualizar dados quando a activity vem para o primeiro plano
    }

    override fun onPause() {
        super.onPause()
        // Salvar dados de rascunho
    }
}

(2) Diagrama de Estados do Ciclo de Vida

100%
stateDiagram-v2
    [*] --> Created: onCreate()
    Created --> Started: onStart()
    Started --> Resumed: onResume()
    Resumed --> Started: onPause()
    Started --> Created: onStop()
    Created --> [*]: onDestroy()
    Started --> Resumed: onResume()

(3) Comparação de Callbacks do Ciclo de Vida

Callback Gatilho Operação Típica
onCreate(bundle) Activity criada Inicializar UI, ViewModel
onStart() Visível mas não interativa Registrar listeners
onResume() Ganha foco Iniciar animações, atualizar dados
onPause() Perde foco Pausar animações, salvar rascunho
onStop() Não mais visível Liberar recursos
onDestroy() Sendo destruída Limpar recursos

4. Jetpack Compose

(1) Funções Composable

KOTLIN
@Composable
fun OrderCard(order: Order) {
    Card(
        modifier = Modifier.fillMaxWidth().padding(8.dp),
        elevation = CardDefaults.cardElevation(defaultElevation = 2.dp)
    ) {
        Row(
            modifier = Modifier.padding(16.dp),
            horizontalArrangement = Arrangement.SpaceBetween
        ) {
            Column {
                Text(text = order.id, style = MaterialTheme.typography.titleMedium)
                Text(text = order.customer, style = MaterialTheme.typography.bodyMedium)
            }
            Text(
                text = "\$${order.total} USD",
                style = MaterialTheme.typography.titleLarge
            )
        }
    }
}

(2) Listas e Estado

KOTLIN
@Composable
fun OrderListScreen(viewModel: OrderViewModel = viewModel()) {
    val orders by viewModel.orders.collectAsState()
    val isLoading by viewModel.isLoading.collectAsState()

    if (isLoading) {
        CircularProgressIndicator()
    } else {
        LazyColumn {
            items(orders) { order ->
                OrderCard(order)
            }
        }
    }
}

(3) Pipeline de Renderização do Compose

100%
flowchart TD
    A[Mudança de Estado] --> B[Composition<br/>Executar funções @Composable]
    B --> C[Layout<br/>Medir e posicionar]
    C --> D[Drawing<br/>Renderizar no canvas]
    D --> E[UI Atualizada]

(4) Comparação XML vs Compose

Dimensão XML + View Jetpack Compose
Paradigma Imperativo Declarativo
Linguagem XML + Kotlin Kotlin puro
Sincronização de estado Manual Automática
Volume de código Alto (XML + Adapter + ViewHolder) Baixo (@Composable)
Preview Requer execução Preview em tempo real no IDE
Curva de aprendizado Moderada Moderada (novo paradigma)

5. ViewModel + StateFlow

(1) ViewModel

KOTLIN
class OrderViewModel : ViewModel() {
    private val _orders = MutableStateFlow<List<Order>>(emptyList())
    val orders: StateFlow<List<Order>> = _orders.asStateFlow()

    private val _isLoading = MutableStateFlow(false)
    val isLoading: StateFlow<Boolean> = _isLoading.asStateFlow()

    fun loadOrders() {
        viewModelScope.launch {
            _isLoading.value = true
            try {
                _orders.value = orderRepository.findAll()
            } finally {
                _isLoading.value = false
            }
        }
    }
}

(2) StateFlow vs LiveData

Dimensão LiveData StateFlow
Valor inicial Não obrigatório Obrigatório
Suporte a corrotinas Precisa de adaptadores Nativo
Thread Thread principal Qualquer thread
Ciclo de vida Consciente Precisa de repeatOnLifecycle
Recomendação Código legado Novos projetos

6. Banco de Dados Room

(1) Entity + Dao

KOTLIN
@Entity(tableName = "orders")
data class OrderEntity(
    @PrimaryKey val id: String,
    val total: Double,
    val status: String,
    val customer: String,
    val createdAt: Long = System.currentTimeMillis()
)

@Dao
interface OrderDao {
    @Query("SELECT * FROM orders ORDER BY createdAt DESC")
    fun getAllOrders(): Flow<List<OrderEntity>>

    @Query("SELECT * FROM orders WHERE id = :id")
    suspend fun getById(id: String): OrderEntity?

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    suspend fun insert(order: OrderEntity)

    @Delete
    suspend fun delete(order: OrderEntity)
}

(2) Componentes do Room

Componente Anotação Responsabilidade
Entity @Entity Mapeamento de tabela do banco de dados
Dao @Dao Operações de acesso a dados
Database @Database Ponto de entrada do banco de dados

7. Exemplo Completo: Painel de Pedidos Android

KOTLIN
// ============================================
// OrderProcessor - Painel de Pedidos Android
// Funcionalidade: Compose UI + ViewModel + StateFlow
// ============================================

// --- Domínio ---
data class Order(val id: String, val total: Double, val status: String, val customer: String)

// --- ViewModel ---
class OrderViewModel : ViewModel() {
    private val _orders = MutableStateFlow<List<Order>>(emptyList())
    val orders: StateFlow<List<Order>> = _orders.asStateFlow()

    private val _isLoading = MutableStateFlow(false)
    val isLoading: StateFlow<Boolean> = _isLoading.asStateFlow()

    init {
        loadOrders()
    }

    fun loadOrders() {
        viewModelScope.launch {
            _isLoading.value = true
            delay(500)  // Simular rede
            _orders.value = listOf(
                Order("ORD-001", 299.99, "CONFIRMED", "Alice"),
                Order("ORD-002", 15_000.00, "PENDING", "Bob"),
                Order("ORD-003", 2_500.00, "SHIPPED", "Charlie"),
                Order("ORD-004", 8_900.00, "CONFIRMED", "Bob"),
                Order("ORD-005", 45.50, "CANCELLED", "Alice")
            )
            _isLoading.value = false
        }
    }

    fun totalRevenue(): Double = _orders.value
        .filter { it.status != "CANCELLED" }
        .sumOf { it.total }
}

// --- Compose UI (Conceitual - requer runtime Android) ---
// @Composable
// fun OrderScreen(viewModel: OrderViewModel = viewModel()) {
//     val orders by viewModel.orders.collectAsState()
//     val isLoading by viewModel.isLoading.collectAsState()
//
//     Column(modifier = Modifier.fillMaxSize().padding(16.dp)) {
//         Text("Order Dashboard", style = MaterialTheme.typography.headlineMedium)
//         Text("Revenue: \$${viewModel.totalRevenue()} USD", style = MaterialTheme.typography.titleLarge)
//
//         if (isLoading) {
//             CircularProgressIndicator()
//         } else {
//             LazyColumn {
//                 items(orders) { order -> OrderCard(order) }
//             }
//         }
//     }
// }

// --- Demo JVM (simulando a lógica do ViewModel) ---
fun main() {
    val viewModel = OrderViewModel()

    // Simular observação de estado
    Thread.sleep(1000)  // Esperar carregamento

    println("=== Painel de Pedidos Android ===")
    println("Receita: \$${viewModel.totalRevenue()} USD")
    println("Carregando: ${viewModel.isLoading.value}")

    viewModel.orders.value.forEach { order ->
        val icon = when (order.status) {
            "CONFIRMED" -> "✅"
            "PENDING" -> "⏳"
            "SHIPPED" -> "📦"
            "CANCELLED" -> "❌"
            else -> "?"
        }
        println("  $icon ${order.id}: \$${order.total} USD (${order.customer}) [${order.status}]")
    }
}

Saída:

TEXT 📖 Somente leitura
=== Painel de Pedidos Android ===
Receita: $26699.99 USD
Carregando: false
  ✅ ORD-001: $299.99 USD (Alice) [CONFIRMED]
  ⏳ ORD-002: $15000.0 USD (Bob) [PENDING]
  📦 ORD-003: $2500.0 USD (Charlie) [SHIPPED]
  ✅ ORD-004: $8900.0 USD (Bob) [CONFIRMED]
  ❌ ORD-005: $45.5 USD (Alice) [CANCELLED]

9. Exemplos práticos rápidos

▶ Exemplo: Activity básica

KOTLIN
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.*
import androidx.compose.material3.*
import androidx.compose.runtime.*

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            MaterialTheme {
                Surface {
                    OrderScreen()
                }
            }
        }
    }
}

@Composable
fun OrderScreen() {
    var orderId by remember { mutableStateOf("") }
    Column(modifier = Modifier.padding(16.dp)) {
        Text("Pedido", style = MaterialTheme.typography.headlineMedium)
        OutlinedTextField(
            value = orderId,
            onValueChange = { orderId = it },
            label = { Text("ID do pedido") }
        )
        Button(onClick = { println("Processando $orderId") }) {
            Text("Confirmar")
        }
    }
}

▶ Exemplo: ViewModel + StateFlow

KOTLIN
import androidx.lifecycle.*
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch

data class OrderUiState(
    val orderId: String = "",
    val total: Double = 0.0,
    val isLoading: Boolean = false,
    val error: String? = null
)

class OrderViewModel : ViewModel() {
    private val _state = MutableStateFlow(OrderUiState())
    val state: StateFlow<OrderUiState> = _state.asStateFlow()

    fun updateId(id: String) {
        _state.value = _state.value.copy(orderId = id)
    }

    fun loadOrder() {
        viewModelScope.launch {
            _state.value = _state.value.copy(isLoading = true)
            try {
                _state.value = _state.value.copy(total = 299.99, isLoading = false)
            } catch (e: Exception) {
                _state.value = _state.value.copy(error = e.message, isLoading = false)
            }
        }
    }
}

// Em Composable
@Composable
fun OrderScreenWithVm(viewModel: OrderViewModel = viewModel()) {
    val state by viewModel.state.collectAsStateWithLifecycle()

    Column {
        Text("Pedido: ${state.orderId}")
        if (state.isLoading) CircularProgressIndicator()
        state.error?.let { Text("Erro: $it") }
    }
}

▶ Exemplo: RecyclerView Adapter com ListAdapter

KOTLIN
import android.view.*
import androidx.recyclerview.widget.*
import android.widget.TextView

data class OrderItem(val id: String, val total: Double)

class OrderAdapter : ListAdapter<OrderItem, OrderAdapter.VH>(DiffCallback) {

    class VH(view: View) : RecyclerView.ViewHolder(view) {
        val id: TextView = view.findViewById(R.id.id)
        val total: TextView = view.findViewById(R.id.total)
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): VH {
        val view = LayoutInflater.from(parent.context)
            .inflate(R.layout.item_order, parent, false)
        return VH(view)
    }

    override fun onBindViewHolder(holder: VH, position: Int) {
        val item = getItem(position)
        holder.id.text = item.id
        holder.total.text = "$%.2f".format(item.total)
    }

    object DiffCallback : DiffUtil.ItemCallback<OrderItem>() {
        override fun areItemsTheSame(a: OrderItem, b: OrderItem) = a.id == b.id
        override fun areContentsTheSame(a: OrderItem, b: OrderItem) = a == b
    }
}

// Uso
val adapter = OrderAdapter()
adapter.submitList(listOf(
    OrderItem("ORD-001", 299.99),
    OrderItem("ORD-002", 1500.00),
    OrderItem("ORD-003", 50.00)
))

▶ Exemplo: Coroutines + Retrofit

KOTLIN
import retrofit2.*
import retrofit2.http.*
import kotlinx.coroutines.*

interface OrderApi {
    @GET("orders/{id}")
    suspend fun getOrder(@Path("id") id: String): Order

    @POST("orders")
    suspend fun createOrder(@Body order: Order): Order
}

class OrderRepository(private val api: OrderApi) {
    suspend fun loadOrder(id: String): Result<Order> {
        return runCatching {
            withTimeout(5000) { api.getOrder(id) }
        }
    }

    suspend fun createOrder(order: Order): Order? {
        return try {
            api.createOrder(order)
        } catch (e: Exception) {
            println("Erro: ${e.message}")
            null
        }
    }
}

// ViewModel
class OrdersViewModel(private val repo: OrderRepository) : ViewModel() {
    fun load(id: String) {
        viewModelScope.launch {
            repo.loadOrder(id)
                .onSuccess { println("OK: $it") }
                .onFailure { println("Falha: ${it.message}") }
        }
    }
}

▶ Exemplo: Room database

KOTLIN
import androidx.room.*

@Entity
data class OrderEntity(
    @PrimaryKey val id: String,
    val total: Double,
    val status: String
)

@Dao
interface OrderDao {
    @Query("SELECT * FROM OrderEntity")
    suspend fun getAll(): List<OrderEntity>

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    suspend fun upsert(order: OrderEntity)

    @Query("UPDATE OrderEntity SET status = :status WHERE id = :id")
    suspend fun updateStatus(id: String, status: String)
}

@Database(entities = [OrderEntity::class], version = 1)
abstract class AppDatabase : RoomDatabase() {
    abstract fun orderDao(): OrderDao
}

// Uso
val db = Room.databaseBuilder(
    context, AppDatabase::class.java, "orders.db"
).build()

viewModelScope.launch {
    val order = OrderEntity("ORD-001", 299.99, "PENDING")
    db.orderDao().upsert(order)
    val all = db.orderDao().getAll()
    println("Total: ${all.size}")
}

▶ Exemplo: Navigation Compose

KOTLIN
import androidx.navigation.compose.*

@Composable
fun AppNavigation() {
    val navController = rememberNavController()

    NavHost(navController, startDestination = "home") {
        composable("home") {
            HomeScreen(onNavigate = { route -> navController.navigate(route) })
        }
        composable("order/{id}") { backStack ->
            val id = backStack.arguments?.getString("id") ?: ""
            OrderDetailScreen(orderId = id)
        }
        composable("settings") {
            SettingsScreen()
        }
    }
}

@Composable
fun HomeScreen(onNavigate: (String) -> Unit) {
    Column {
        Button(onClick = { onNavigate("order/ORD-001") }) {
            Text("Ver pedido")
        }
        Button(onClick = { onNavigate("settings") }) {
            Text("Configurações")
        }
    }
}

▶ Exemplo: Estado do Compose

KOTLIN
import androidx.compose.runtime.*

@Composable
fun CounterScreen() {
    // remember sobrevive recomposições, mas não navegação
    var count by remember { mutableStateOf(0) }

    // rememberSaveable persiste em mudanças de configuração
    var saved by rememberSaveable { mutableStateOf("initialized") }

    Column(modifier = Modifier.padding(16.dp)) {
        Text("Count: $count")
        Text("Saved: $saved")
        Button(onClick = { count++ }) {
            Text("Incrementar")
        }
        Button(onClick = { saved = "mod-${count}" }) {
            Text("Salvar")
        }
    }
}

❓ Perguntas Frequentes

P: Preciso usar Compose? Posso continuar usando XML? R: Você pode continuar usando XML, mas o Google recomenda Compose como padrão para novos projetos. Compose e XML podem coexistir (use ComposeView para incorporar Compose no XML).

P: Qual a diferença entre ViewModel e Presenter? R: ViewModel não é destruído em mudanças de configuração (rotação de tela) — dados são automaticamente retidos. Um Presenter requer salvamento/restauração manual em mudanças de configuração.

P: Como escolher entre StateFlow e SharedFlow? R: StateFlow é para manter estado (deve ter um valor inicial, deduplica). SharedFlow é para fluxos de eventos (multicast, sem valor inicial). Use StateFlow para estado de UI, SharedFlow para eventos únicos.

P: O Room suporta corrotinas? R: Sim. Funções suspend do Dao executam automaticamente no pool de threads do Room. Consultas que retornam Flow re-emitem automaticamente quando os dados mudam.

P: Como é a performance do Compose? R: Compose usa recomposição inteligente (redesenha apenas os Composables alterados), com performance próxima a Views escritas manualmente. Evite cálculos dentro de Composable — use remember e derivedStateOf para cache.

P: Como lidar com navegação no Compose? R: Use NavHost + composable() para definir um grafo de navegação, navController.navigate() para navegar. Navegação type-safe está disponível no Compose 1.0+.


📖 Resumo


📝 Exercícios

  1. Iniciante (⭐): Crie uma função Compose Greeting que exibe "Hello, $name!". Dica: @Composable fun Greeting(name: String) { Text("Hello, $name!") }
  2. Intermediário (⭐⭐): Use ViewModel + StateFlow para implementar um contador com UI Compose mostrando a contagem em tempo real. Dica: MutableStateFlow(0) + collectAsState()
  3. Avançado (⭐⭐⭐): Implemente uma UI Compose completa de lista de pedidos: LazyColumn exibindo pedidos, pull-to-refresh, clique para navegar aos detalhes. Dica: LazyColumn + pullRefresh + NavController

← Anterior | Próximo →

Web-Tutorial.com

Equipe Técnica Web-Tutorial

Uma plataforma de tutoriais mantida por diversos desenvolvedores. Cada tutorial é escrito e revisado por profissionais da área correspondente. Trabalhamos para manter nosso conteúdo preciso e confiável — se encontrar algum problema, avise-nos.

100%