Programação Reativa em Kotlin: o poder do kotlinx.coroutines e Flow
Programação reativa é um paradigma focado no processamento assíncrono de fluxos de dados e na propagação de mudanças. No ecossistema Kotlin, a principal ferramenta para implementar padrões reativos é a biblioteca kotlinx.coroutines, mais especificamente seu componente Flow. Ao contrário do RxJava, o Flow é totalmente integrado com corrotinas, tornando o código mais conciso e seguro.
Conceitos fundamentais do Flow
Flow é um fluxo de dados assíncrono frio que emite valores sequencialmente. Ele suporta:
- Fluxos frios — os dados começam a ser gerados apenas quando há um assinante (operador terminal).
- Contexto de execução — o fluxo pode alternar entre diferentes dispatchers de corrotinas.
- Cancelamento via corrotinas — o cancelamento da corrotina pai encerra automaticamente o Flow.
Instalação e configuração
Para usar o Flow, adicione a dependência no build.gradle.kts:
dependencies { implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.1")}Criação e consumo de Flow
Vamos criar um fluxo simples de números com atraso:
import kotlinx.coroutines.*import kotlinx.coroutines.flow.*
fun simpleFlow(): Flow<Int> = flow { for (i in 1..5) { delay(100) // simulação de trabalho assíncrono emit(i) // emite o valor }}
fun main() = runBlocking { simpleFlow().collect { value -> println("Recebido: $value") }}Neste exemplo, flow { ... } cria um fluxo frio, e collect é o operador terminal que inicia a execução.
Transformação de dados com operadores
O Flow oferece um rico conjunto de operadores, semelhantes ao RxJava:
map— transforma cada valor.filter— filtra valores por condição.flatMapConcat— combina fluxos sequencialmente.catch— trata exceções dentro do fluxo.
Exemplo com operadores:
fun transformFlow(): Flow<String> = flow { emit("Kotlin") emit("Java") emit("Python")}.map { it.lowercase() } .filter { it.startsWith("k") }
fun main() = runBlocking { transformFlow().collect { println(it) } // Saída: kotlin}Tratamento de erros e conclusão
O Flow permite tratar exceções elegantemente com catch e executar ações finais com onCompletion:
fun errorProneFlow(): Flow<Int> = flow { emit(1) emit(2) throw RuntimeException("Erro!") emit(3) // não será executado}
fun main() = runBlocking { errorProneFlow() .catch { e -> println("Erro capturado: ${e.message}") } .onCompletion { println("Fluxo concluído") } .collect { println(it) }}// Saída: 1, 2, Erro capturado: Erro!, Fluxo concluídoFluxos quentes: StateFlow e SharedFlow
Ao contrário do Flow frio, StateFlow e SharedFlow são fluxos quentes que armazenam estado e podem ter vários assinantes.
StateFlow
Usado para armazenar e observar estado (por exemplo, em ViewModel):
class CounterViewModel { private val _counter = MutableStateFlow(0) val counter: StateFlow<Int> = _counter.asStateFlow()
fun increment() { _counter.value++ }}
// Em Activity ou FragmentlifecycleScope.launch { viewModel.counter.collect { value -> textView.text = "Contagem: $value" }}SharedFlow
Adequado para
Também na biblioteca
Componente de Navegação para Kotlin: guia completo com exemplos
Guia completo do Chucker para Kotlin com exemplos práticos
Envio de email em C# (.NET) usando SmtpClient e MailKit
Aprendizado de Máquina em Rust: visão geral das bibliotecas para ML e Data Science