---
title: "Kotlin Flow Tutorial em Português — Passo a Passo | Kotlin Brasil"
url: "https://kotlin.dev.br/tutoriais/kotlin-flow-tutorial/"
markdown_url: "https://kotlin.dev.br/tutoriais/kotlin-flow-tutorial.MD"
description: "Aprenda Kotlin Flow do zero: cold streams, operadores map/filter, StateFlow, SharedFlow, flowOn e tratamento de erros com exemplos práticos."
date: "2025-07-03"
author: "Karina Melo"
---

# Kotlin Flow Tutorial em Português — Passo a Passo | Kotlin Brasil

Aprenda Kotlin Flow do zero: cold streams, operadores map/filter, StateFlow, SharedFlow, flowOn e tratamento de erros com exemplos práticos.


Neste tutorial completo, você vai aprender tudo sobre **Kotlin Flow**, a API de programação reativa do Kotlin para lidar com fluxos de dados assíncronos. Vamos cobrir desde os conceitos fundamentais de cold streams até tópicos avançados como [StateFlow](/glossario/flow/), SharedFlow, operadores de transformação e tratamento de exceções. Ao final, você terá domínio suficiente para aplicar Flow em projetos Android e backend com confiança.

## O que é Kotlin Flow?

O [Flow](/glossario/flow/) é uma API da biblioteca `kotlinx.coroutines` que permite trabalhar com sequências de valores emitidos de forma assíncrona. Ele é classificado como um **cold stream** — isso significa que o código produtor só é executado quando existe um coletor (collector) consumindo os dados. Essa característica diferencia o Flow de outras abordagens como Channels, que são hot streams.

Para quem vem do mundo RxJava, o Flow é a alternativa nativa do Kotlin para programação reativa, com a vantagem de ser muito mais leve, integrado com [Coroutines](/glossario/coroutine/) e com tipagem segura graças ao sistema de tipos do Kotlin.

## Passo 1: Criando seu Primeiro Flow com o Builder

O jeito mais comum de criar um Flow é usando o builder `flow {}`. Dentro dele, você usa a [função suspend](/glossario/suspend/) `emit()` para enviar valores ao coletor.

```kotlin
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.delay

fun contagem(): Flow<Int> = flow {
    for (i in 1..5) {
        delay(500) // simula trabalho assíncrono
        emit(i)    // emite o valor
    }
}

fun main() = runBlocking {
    contagem().collect { valor ->
        println("Valor recebido: $valor")
    }
}
```

Note que `collect` é uma **função terminal** — ela é [suspend](/glossario/suspend/) e bloqueia a [coroutine](/glossario/coroutine/) atual até que o Flow termine de emitir todos os valores. O Flow respeita a structured concurrency, sendo cancelado automaticamente quando o escopo da coroutine é encerrado.

Existem também builders simplificados para casos comuns:

```kotlin
// Flow a partir de uma lista
val flowDeLista = listOf(1, 2, 3).asFlow()

// Flow com um unico valor
val flowUnico = flowOf("Olá, Kotlin Flow!")

// Flow vazio
val flowVazio = emptyFlow<String>()
```

## Passo 2: Operadores Intermediários — map, filter e transform

Assim como trabalhamos com [Collections](/glossario/collections/), o Flow oferece operadores intermediários que transformam os dados antes de chegarem ao coletor. Esses operadores retornam um novo Flow e são avaliados de forma preguiçosa (lazy).

```kotlin
fun main() = runBlocking {
    (1..10).asFlow()
        .filter { it % 2 == 0 }       // mantém apenas pares
        .map { it * it }               // eleva ao quadrado
        .collect { println(it) }       // 4, 16, 36, 64, 100
}
```

O operador `transform` é mais flexível, permitindo emitir zero ou mais valores para cada item recebido:

```kotlin
fun main() = runBlocking {
    (1..3).asFlow()
        .transform { valor ->
            emit("Processando $valor...")
            delay(300)
            emit("Resultado: ${valor * 10}")
        }
        .collect { println(it) }
}
```

Outros operadores úteis incluem `take` (limita quantidade de emissões), `drop` (ignora as primeiras N emissões), `distinctUntilChanged` (evita valores duplicados consecutivos) e `onEach` (executa uma ação para cada valor sem transformá-lo).

## Passo 3: Controlando o Contexto com flowOn

Por padrão, o Flow executa no contexto da [coroutine](/glossario/coroutine/) que chama `collect`. Mas e se você quiser que o código produtor rode em uma thread diferente? É aí que entra o operador `flowOn`.

```kotlin
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.runBlocking

fun dadosPesados(): Flow<Int> = flow {
    for (i in 1..5) {
        Thread.sleep(500) // simula operacao de I/O
        emit(i)
        println("Emitindo em: ${Thread.currentThread().name}")
    }
}.flowOn(Dispatchers.IO) // muda o contexto do produtor

fun main() = runBlocking {
    dadosPesados().collect { valor ->
        println("Coletando $valor em: ${Thread.currentThread().name}")
    }
}
```

O `flowOn` altera **apenas** o contexto upstream (do produtor), não do coletor. Isso mantém a previsibilidade do código e respeita o princípio de transparência de contexto do Flow.

## Passo 4: Buffer e Conflate para Performance

Quando o produtor é mais rápido que o coletor, podemos usar `buffer` para permitir que eles executem concorrentemente, ou `conflate` para descartar valores intermediários e processar apenas o mais recente.

```kotlin
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.delay

fun main() = runBlocking {
    val tempoInicio = System.currentTimeMillis()

    (1..5).asFlow()
        .onEach { delay(100) }  // produtor emite a cada 100ms
        .buffer()                // permite execucao concorrente
        .collect { valor ->
            delay(300)           // coletor leva 300ms para processar
            val tempo = System.currentTimeMillis() - tempoInicio
            println("$valor coletado em ${tempo}ms")
        }
}
```

Com `conflate`, se o coletor estiver ocupado, valores intermediários são descartados:

```kotlin
(1..5).asFlow()
    .onEach { delay(100) }
    .conflate()  // descarta valores nao processados
    .collect { valor ->
        delay(300)
        println("Processado: $valor") // pode pular 2, 3, 4
    }
```

## Passo 5: StateFlow e SharedFlow — Hot Streams

Enquanto o Flow padrão é cold, o Kotlin oferece duas variantes hot para cenários onde múltiplos coletores precisam observar o mesmo fluxo de dados.

**StateFlow** mantém sempre o valor mais recente e emite para novos coletores imediatamente. É ideal para representar estado em arquiteturas como MVVM.

```kotlin
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.delay

fun main() = runBlocking {
    val estado = MutableStateFlow(0) // valor inicial obrigatório

    // Coletor 1
    val job1 = launch {
        estado.collect { println("Coletor 1: $it") }
    }

    delay(100)
    estado.value = 1
    delay(100)
    estado.value = 2

    // Coletor 2 recebe imediatamente o valor atual (2)
    val job2 = launch {
        estado.collect { println("Coletor 2: $it") }
    }

    delay(100)
    estado.value = 3

    delay(200)
    job1.cancel()
    job2.cancel()
}
```

**SharedFlow** é mais flexível — permite configurar replay (quantos valores anteriores novos coletores recebem) e não exige valor inicial:

```kotlin
val eventos = MutableSharedFlow<String>(
    replay = 1,           // novos coletores recebem o último evento
    extraBufferCapacity = 5
)

// Emitir valor
eventos.emit("Evento A")

// Ou de forma nao-suspensa (pode falhar se o buffer estiver cheio)
eventos.tryEmit("Evento B")
```

## Passo 6: Tratamento de Exceções com catch

O operador `catch` intercepta exceções que ocorrem upstream no Flow. Ele não captura erros no `collect`, apenas nos operadores e no produtor acima dele na cadeia.

```kotlin
fun fluxoComErro(): Flow<Int> = flow {
    emit(1)
    emit(2)
    throw RuntimeException("Algo deu errado!")
    emit(3) // nunca será alcançado
}

fun main() = runBlocking {
    fluxoComErro()
        .catch { e ->
            println("Erro capturado: ${e.message}")
            emit(-1) // pode emitir um valor de fallback
        }
        .collect { println("Valor: $it") }
    // Saída: Valor: 1, Valor: 2, Erro capturado: Algo deu errado!, Valor: -1
}
```

Para capturar erros no `collect`, use `onEach` junto com `catch` e `launchIn`:

```kotlin
fluxoComErro()
    .onEach { valor -> println("Processando: $valor") }
    .catch { e -> println("Erro: ${e.message}") }
    .launchIn(this) // equivale a launch { flow.collect() }
```

Você também pode usar `onCompletion` para executar código quando o Flow termina, independentemente de ter ocorrido erro:

```kotlin
(1..3).asFlow()
    .onCompletion { causa ->
        if (causa != null) println("Terminou com erro: $causa")
        else println("Flow completado com sucesso")
    }
    .collect { println(it) }
```

## Erros Comuns

1. **Emitir valores de outro contexto sem flowOn**: Nunca use `withContext` dentro do builder `flow {}` para mudar o dispatcher. O Flow proíbe isso e lança uma `IllegalStateException`. Use `flowOn` em vez disso.

2. **Esquecer que collect é suspend**: O `collect` suspende a coroutine. Se você precisa coletar sem bloquear, use `launchIn(scope)` em vez de `collect`.

3. **Confundir StateFlow com LiveData**: O [StateFlow](/glossario/flow/) sempre emite o valor atual para novos coletores, mas diferente do LiveData, ele não é lifecycle-aware. No Android, use `repeatOnLifecycle` ou `flowWithLifecycle` para coletar de forma segura.

4. **Não tratar backpressure**: Se o produtor é muito mais rápido que o consumidor e você não usa `buffer` ou `conflate`, o coletor será o gargalo e o Flow ficará lento.

5. **Usar SharedFlow sem buffer adequado**: Se o `extraBufferCapacity` for 0 e nenhum coletor estiver ativo, chamadas a `emit()` vão suspender indefinidamente. Configure o buffer conforme sua necessidade.

6. **Ignorar o cancelamento**: Flows respeitam o cancelamento cooperativo de coroutines. Se você usar operações bloqueantes (como `Thread.sleep`) em vez de `delay`, o cancelamento não funcionará corretamente.

## Conclusão e Próximos Passos

Neste tutorial, você aprendeu os fundamentos e técnicas avançadas do Kotlin Flow: desde a criação de cold streams com o builder `flow {}`, passando por operadores de transformação como `map`, `filter` e `transform`, controle de contexto com `flowOn`, otimização de performance com `buffer` e `conflate`, hot streams com StateFlow e SharedFlow, até o tratamento robusto de exceções com `catch`.

O Flow é uma peça central no ecossistema Kotlin moderno e se integra perfeitamente com frameworks como Jetpack Compose, Room e Retrofit. Como próximos passos, recomendamos:

- Explorar a integração de Flow com [Room Database](/tutoriais/kotlin-room-database-tutorial/) para observar mudanças no banco de dados em tempo real
- Aprender sobre o padrão [MVVM](/tutoriais/kotlin-mvvm-tutorial/) usando StateFlow no ViewModel
- Praticar combinando múltiplos Flows com operadores como `combine`, `zip` e `merge`
- Consultar o [glossário de Flow](/glossario/flow/) e [Coroutines](/glossario/coroutine/) para reforçar conceitos

Dominar o Kotlin Flow é um diferencial importante para qualquer desenvolvedor que trabalhe com Kotlin, seja no Android ou no backend.
