---
title: "Coroutines Avançadas: Flow e Channel Tutorial em Português — Passo a Passo | Kotlin Brasil"
url: "https://kotlin.dev.br/tutoriais/coroutines-avancado/"
markdown_url: "https://kotlin.dev.br/tutoriais/coroutines-avancado.MD"
description: "Aprenda Coroutines avançadas em Kotlin com Flow e Channel. Tutorial completo com exemplos práticos de programação assíncrona."
date: "2025-12-01"
author: "Karina Melo"
---

# Coroutines Avançadas: Flow e Channel Tutorial em Português — Passo a Passo | Kotlin Brasil

Aprenda Coroutines avançadas em Kotlin com Flow e Channel. Tutorial completo com exemplos práticos de programação assíncrona.


Neste tutorial, vamos explorar os conceitos avançados de [Coroutines](/glossario/coroutine/) em Kotlin, com foco em [Flow](/glossario/flow/) e [Channel](/glossario/channel/). Essas duas ferramentas são essenciais para lidar com fluxos de dados assíncronos de forma eficiente e elegante. Se você já domina o básico de coroutines, este é o próximo passo para elevar seu nível como desenvolvedor Kotlin.

## O que são Flows em Kotlin?

O [Flow](/glossario/flow/) é um tipo de stream assíncrono e frio (cold stream) que emite valores sequencialmente. Diferente de um [Channel](/glossario/channel/), o Flow só começa a produzir valores quando alguém os coleta. Isso o torna ideal para cenários em que você precisa processar uma sequência de dados de forma reativa.

Um Flow é declarado usando o builder `flow {}`, e cada valor é emitido com a função [suspend](/glossario/suspend/) `emit()`. O coletor recebe os valores usando `collect {}`.

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

fun numerosSimples(): Flow<Int> = flow {
    for (i in 1..5) {
        kotlinx.coroutines.delay(300)
        emit(i)
    }
}

fun main() = runBlocking {
    numerosSimples().collect { valor ->
        println("Recebido: $valor")
    }
}
```

Neste exemplo, o Flow emite os números de 1 a 5, com um atraso de 300 milissegundos entre cada emissão. O `collect` é uma função terminal que consome os valores emitidos. Note que o Flow respeita a structured concurrency do Kotlin, ou seja, ele é cancelado automaticamente quando o escopo da coroutine é cancelado.

Os Flows são cold streams, o que significa que o código dentro do builder `flow {}` só é executado quando alguém chama `collect`. Isso é diferente de Channels, que são hot streams e podem produzir valores independentemente de haver um consumidor.

## Operadores de Flow

Uma das maiores vantagens do Flow é a rica coleção de operadores intermediários disponíveis. Eles permitem transformar, filtrar e combinar fluxos de dados de maneira declarativa, semelhante ao que fazemos com [Collections](/glossario/collections/).

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

fun main() = runBlocking {
    val fluxo = (1..20).asFlow()

    fluxo
        .filter { it % 2 == 0 }
        .map { it * it }
        .take(5)
        .collect { valor ->
            println("Valor transformado: $valor")
        }
}
```

Alguns dos operadores mais utilizados são:

- **`map`**: transforma cada valor emitido pelo Flow.
- **`filter`**: filtra valores com base em uma condição.
- **`take`**: limita a quantidade de valores emitidos.
- **`zip`**: combina dois Flows em pares.
- **`flatMapConcat`**: transforma cada valor em um novo Flow e os concatena.
- **`onEach`**: executa uma ação para cada valor sem alterá-lo.
- **`catch`**: intercepta exceções lançadas upstream.
- **`flowOn`**: altera o dispatcher em que o Flow é executado.

O operador `flowOn` é particularmente importante, pois permite que você mude o contexto de execução do Flow sem afetar o coletor. Por exemplo, você pode processar dados em `Dispatchers.IO` e coletar na thread principal.

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

fun buscarDados(): Flow<String> = flow {
    emit("Dado 1")
    emit("Dado 2")
    emit("Dado 3")
}.flowOn(Dispatchers.IO)

fun main() = runBlocking {
    buscarDados()
        .catch { e -> println("Erro capturado: ${e.message}") }
        .collect { dado ->
            println("Coletado na Main: $dado")
        }
}
```

## Channels: Comunicação entre Coroutines

Enquanto o Flow é um cold stream, o [Channel](/glossario/channel/) é um hot stream que permite a comunicação entre coroutines. Pense em um Channel como uma fila (queue) thread-safe que conecta produtores e consumidores de dados.

Channels são úteis quando você precisa de comunicação bidirecional entre coroutines ou quando deseja que o produtor funcione independentemente do consumidor. Existem diferentes tipos de Channel:

- **`Channel.RENDEZVOUS`** (padrão): sem buffer, o produtor espera o consumidor estar pronto.
- **`Channel.BUFFERED`**: com buffer limitado (64 por padrão).
- **`Channel.UNLIMITED`**: buffer ilimitado, nunca suspende o produtor.
- **`Channel.CONFLATED`**: mantém apenas o último valor enviado.

```kotlin
import kotlinx.coroutines.*
import kotlinx.coroutines.channels.*

fun main() = runBlocking {
    val channel = Channel<Int>(capacity = Channel.BUFFERED)

    launch {
        for (i in 1..10) {
            println("Enviando: $i")
            channel.send(i)
            delay(100)
        }
        channel.close()
    }

    launch {
        for (valor in channel) {
            println("Recebendo: $valor")
            delay(250)
        }
    }
}
```

Neste exemplo, o produtor envia valores mais rápido do que o consumidor processa. Graças ao buffer, o produtor pode adiantar o envio de alguns valores sem precisar aguardar o consumidor. Quando o buffer está cheio, o produtor é suspenso automaticamente até que haja espaço.

É fundamental lembrar de fechar o Channel com `close()` quando não há mais dados a enviar. Caso contrário, o consumidor ficará suspenso indefinidamente esperando novos valores.

## StateFlow e SharedFlow

O Kotlin também oferece dois tipos especiais de Flow para cenários de estado e eventos:

**StateFlow** é um Flow que mantém um único valor atual e emite atualizações para todos os coletores. Ele é ideal para representar estado em aplicações Android com Jetpack Compose ou ViewModels.

**SharedFlow** é mais flexível e permite configurar replay (quantidade de valores emitidos novamente para novos coletores) e buffer. Ele é perfeito para eventos que não devem ser perdidos.

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

class ContadorViewModel {
    private val _contador = MutableStateFlow(0)
    val contador: StateFlow<Int> = _contador.asStateFlow()

    fun incrementar() {
        _contador.value++
    }

    fun decrementar() {
        _contador.value--
    }
}

fun main() = runBlocking {
    val viewModel = ContadorViewModel()

    val job = launch {
        viewModel.contador.collect { valor ->
            println("Contador: $valor")
        }
    }

    viewModel.incrementar()
    delay(100)
    viewModel.incrementar()
    delay(100)
    viewModel.decrementar()
    delay(100)

    job.cancel()
}
```

A diferença principal entre StateFlow e SharedFlow é que StateFlow sempre possui um valor inicial e utiliza `distinctUntilChanged` por padrão, ou seja, só emite quando o valor realmente muda. SharedFlow não tem valor inicial e pode emitir valores repetidos.

## Dicas e Erros Comuns

1. **Não coletar Flow na Main Thread sem cuidado**: em aplicações Android, sempre colete Flows no escopo correto (`viewModelScope`, `lifecycleScope`) para evitar memory leaks.

2. **Esquecer de fechar Channels**: um Channel não fechado pode causar coroutines suspensas para sempre. Sempre use `close()` ou considere usar `produce {}` que fecha automaticamente.

3. **Usar Channel quando Flow basta**: se você não precisa de comunicação bidirecional ou hot stream, prefira Flow. Ele é mais simples, seguro e compõe melhor com operadores.

4. **Ignorar o operador `catch`**: sem tratamento de erros, exceções em Flows podem derrubar sua aplicação. Sempre adicione `catch {}` antes de `collect {}`.

5. **Confundir `flowOn` com `launchIn`**: o `flowOn` muda o contexto de execução do upstream, enquanto `launchIn` define o escopo para o coletor. Usar ambos incorretamente pode levar a bugs sutis.

6. **Não usar `stateIn` ou `shareIn` em ViewModels**: converter um cold Flow em StateFlow ou SharedFlow com `stateIn` ou `shareIn` evita múltiplas execuções desnecessárias quando há vários coletores.

## Conclusão e Próximos Passos

Neste tutorial, exploramos os conceitos avançados de [Coroutines](/glossario/coroutine/) em Kotlin, incluindo [Flow](/glossario/flow/), [Channel](/glossario/channel/), StateFlow e SharedFlow. Essas ferramentas formam a base da programação assíncrona moderna em Kotlin e são fundamentais para qualquer desenvolvedor que trabalhe com aplicações reativas.

Dominar Flows e Channels permite criar aplicações mais responsivas, com melhor gerenciamento de recursos e código mais limpo. A chave é entender quando usar cada ferramenta: Flow para streams frios e reativos, Channel para comunicação entre coroutines, StateFlow para estado observável e SharedFlow para eventos.

Para continuar sua jornada de aprendizado, recomendamos os seguintes tutoriais:

- [Primeiro App Android com Kotlin](/tutoriais/primeiro-app-android/) para aplicar coroutines em um projeto real
- [Jetpack Compose: Introdução](/tutoriais/jetpack-compose-básico/) para usar StateFlow com UI declarativa
- [Testes Unitários com Kotlin](/tutoriais/testes-unitarios/) para aprender a testar código assíncrono
- [Criando API REST com Ktor](/tutoriais/ktor-api-rest/) para usar Flow em aplicações server-side

Se você quer comparar modelos de concorrência, <a href="https://golang.com.br/" target="_blank" rel="noopener" onclick="umami.track('portfolio-site-click', { destination: 'golang.com.br' })">Go utiliza goroutines com um modelo de concorrência elegante</a> e <a href="https://rustlang.com.br/" target="_blank" rel="noopener" onclick="umami.track('portfolio-site-click', { destination: 'rustlang.com.br' })">Rust oferece async/await com garantias de segurança em tempo de compilação</a>.
