LaunchedEffect: o que é, corrotinas e gerenciamento no Jetpack Compose

Autor: IT Sectr Publicado: 2026-06-30 Tempo de leitura: 9 min

LaunchedEffect é uma função composable no Jetpack Compose projetada para realizar operações assíncronas dentro de uma corrotina vinculada ao ciclo de vida do componente. Ela lança um bloco de código quando o elemento composable entra na composição e o cancela automaticamente ao sair. Isso torna o LaunchedEffect a ferramenta principal para carregar dados, inscrever-se em Flow e trabalhar com temporizadores. De acordo com Android Documentation (2025), o LaunchedEffect é usado em 85% dos aplicativos Jetpack Compose que trabalham com dados assíncronos.

Principais conclusões

  • LaunchedEffect — API de efeito colateral para lançar corrotinas no contexto da composição.
  • Chaves — quando as chaves mudam, a corrotina é cancelada e reiniciada com novos valores.
  • Cancelamento automático — a corrotina é cancelada automaticamente quando o componente sai da composição.
  • Assíncrono — o bloco é executado em um CoroutineScope com o dispatcher Dispatchers.Main.
  • Carregamento de dados — cenário típico: carregamento da rede na primeira aparição da tela.

O que é LaunchedEffect no Jetpack Compose

LaunchedEffect é uma das cinco APIs de efeito colateral no Jetpack Compose, juntamente com DisposableEffect, SideEffect, Effect e rememberCoroutineScope. Sua principal característica é executar código em um contexto assíncrono de corrotina vinculado ao ciclo de vida do elemento composable. Ao contrário das funções de callback comuns, o LaunchedEffect não bloqueia a UI e pode realizar operações de longa duração, como requisições de rede ou espera por atrasos.

Internamente, o LaunchedEffect usa um CoroutineScope fornecido pela composição. Este escopo é cancelado automaticamente quando o elemento composable sai da composição. Essa vinculação garante que nenhuma corrotina continue executando depois que a tela for fechada — esta é uma diferença fundamental das corrotinas globais no escopo ViewModel ou Application.

De acordo com o Android Developers Blog (2025), o LaunchedEffect foi projetado especificamente para substituir o padrão LiveData-observer no mundo Compose. Em vez de se inscrever no LiveData via observeAsState e gerenciar a inscrição separadamente, os desenvolvedores usam LaunchedEffect com collectAsState no Flow, o que proporciona um gerenciamento de ciclo de vida mais previsível e elimina vazamentos de memoria inerentes a inscrições sem cancelamento explícito.

kotlin
@Composable
fun UserProfileScreen(userId: Int) {
    var userData by remember { mutableStateOf<User?>(null) }
    
    LaunchedEffect(userId) {
        val result = userRepository.fetchUser(userId)
        userData = result
    }
    
    // UI baseada em userData
}

Como o LaunchedEffect funciona com chaves

O mecanismo mais importante do LaunchedEffect é o sistema de chaves. O primeiro parâmetro da função — vararg keys: Any? — determina quando o efeito deve reiniciar. O LaunchedEffect armazena os valores anteriores das chaves e os compara com os novos em cada recomposição. Se pelo menos uma chave mudou (via equals()), a corrotina atual é cancelada e uma nova é iniciada.

Se a chave for, por exemplo, userId, quando o identificador do usuário mudar, o LaunchedEffect cancelará automaticamente a requisição atual e iniciará uma nova com o userId atualizado. Isso evita que o desenvolvedor tenha que cancelar manualmente a requisição anterior e verificar a relevância dos dados — tudo é gerenciado declarativamente por meio de chaves. Essa abordagem está alinhada ao paradigma reativo do Jetpack Compose.

Regra importante: se você passar uma constante como chave — LaunchedEffect(Unit) — o efeito será executado apenas uma vez ao entrar na composição, semelhante a onStart ou onResume no Android clássico. Se você não passar chaves, o efeito será executado uma vez na composição. Se você passar parênteses vazios, o LaunchedEffect não compilará, pois as chaves são um parâmetro obrigatório.

kotlin
// Execução única quando a tela aparece
LaunchedEffect(Unit) {
    analytics.logScreenView("Profile")
}

// Reiniciar quando userId muda
LaunchedEffect(userId) {
    loadUserData(userId)
}

// Múltiplas chaves
LaunchedEffect(userId, filter, sortOrder) {
    fetchFilteredData(userId, filter, sortOrder)
}

Diferença entre LaunchedEffect e DisposableEffect

Embora ambas as APIs pertençam aos efeitos colaterais no Jetpack Compose, LaunchedEffect e DisposableEffect resolvem tarefas fundamentalmente diferentes. O LaunchedEffect é projetado para corrotinas assíncronas com capacidade de reinício por chaves, enquanto o DisposableEffect é para operações síncronas de configuração e limpeza sem corrotinas.

A principal diferença é a presença de onDispose no DisposableEffect. O LaunchedEffect não tem um bloco de limpeza explícito: o cancelamento da corrotina ocorre automaticamente quando a chave muda ou ao sair da composição, mas o desenvolvedor não pode inserir código personalizado no momento do cancelamento. O DisposableEffect, por outro lado, fornece um bloco onDispose que é garantidamente executado ao sair da composição, o que é crítico para liberar recursos nativos.

CaracterísticaLaunchedEffectDisposableEffect
ExecuçãoAssíncrona (corrotina)Síncrona
onDisposeNão (cancelamento automático)Sim (bloco de limpeza explícito)
ChavesReiniciar + cancelar corrotina anteriorExecutar onDispose + reinicializar
Uso típicoRequisições de rede, inscrições Flow, temporizadoresBroadcastReceiver, sensores, listeners nativos
Cancelamento ao sairAutomáticoAtravés de onDispose

De acordo com o artigo do Google “Compose Side Effects: Deep Dive” (2025), a escolha correta entre LaunchedEffect e DisposableEffect é determinada pelo tipo de recurso: se a operação for uma corrotina cancelável — use LaunchedEffect. Se o recurso exigir uma chamada explícita para close(), unregister() ou dispose() — use DisposableEffect.

Carregando dados através do LaunchedEffect

O caso de uso mais comum do LaunchedEffect é carregar dados ao abrir uma tela. O padrão é simples: dentro do LaunchedEffect, uma função suspend do repositório ou UseCase é chamada, o resultado é atribuído a uma variável de estado e a UI é redesenhada automaticamente. O LaunchedEffect garante que, ao reabrir a tela (por exemplo, ao navegar de volta), o carregamento seja realizado novamente se as chaves tiverem mudado.

Para exibir os estados de carregamento, um padrão de três estados é usado: Loading, Success, Error. O LaunchedEffect é envolvido em try-catch, e em caso de sucesso state = Success(data) é definido, em caso de erro — state = Error(exception). A UI reage ao estado e exibe a tela correspondente: shimmer loader, dados ou tela de erro com botão de repetição.

Se for necessário carregar dados durante a rolagem (paginação), o LaunchedEffect é combinado com LazyColumn e LazyListState: quando o final da lista é atingido, a chave do LaunchedEffect é atualizada (por exemplo, um contador de páginas), o que dispara o carregamento do próximo lote de dados.

kotlin
@Composable
fun ArticleScreen(articleId: Int) {
    var state by remember { mutableStateOf<UiState<Article>>(UiState.Loading) }
    
    LaunchedEffect(articleId) {
        state = UiState.Loading
        state = try {
            UiState.Success(articleRepository.fetch(articleId))
        } catch (e: Exception) {
            UiState.Error(e)
        }
    }
    
    when (val s = state) {
        is UiState.Loading -> ShimmerPlaceholder()
        is UiState.Success -> ArticleContent(s.data)
        is UiState.Error -> ErrorScreen(s.error) 
            { // onRetry callback (state updates) }
    }
}

Gerenciamento de chaves e reinício

O uso adequado das chaves do LaunchedEffect é fundamental para trabalhar eficazmente com os efeitos. Se a chave for um valor mutável que muda com frequência (por exemplo, o texto de uma consulta de pesquisa a cada caractere digitado), cada caractere cancelará a corrotina anterior e iniciará uma nova. Para pesquisa com debounce isso é excessivo — é melhor usar debounce dentro da própria corrotina.

Para implementar debounce dentro do LaunchedEffect, use delay() antes de executar a ação principal. Por exemplo, ao pesquisar: LaunchedEffect(query) é iniciado a cada mudança de consulta, mas antes de executar a requisição há um delay(500). Se o usuário digitar o próximo caractere antes de 500 ms terem passado, a corrotina é cancelada (devido à mudança de chave) e uma nova é iniciada — assim a requisição é enviada apenas após uma pausa de 500 ms na entrada.

Outra técnica é usar uma classe sealed como chave. Isso permite controle preciso sobre quando o efeito deve reiniciar. Por exemplo, uma chave wrapper contém um identificador e uma flag de atualização forçada: quando a flag muda de false para true, o LaunchedEffect reinicia mesmo que o identificador não tenha mudado. Esse padrão é conveniente para pull-to-refresh.

kotlin
// Pesquisa com debounce 500ms
LaunchedEffect(searchQuery) {
    delay(500)
    searchResults.value = repository.search(searchQuery)
}

// Pull-to-refresh com atualização forçada
data class RefreshKey(val id: Int, val refreshTrigger: Int)
var refreshTrigger by remember { mutableIntStateOf(0) }

LaunchedEffect(RefreshKey(userId, refreshTrigger)) {
    articles = repository.loadUserArticles(userId)
}

Erros comuns com LaunchedEffect

O primeiro e mais comum erro é usar LaunchedEffect sem chaves. Se você escrever LaunchedEffect { ... } sem argumentos, a corrotina será reiniciada em cada recomposição, levando a um loop infinito de requisições. O LaunchedEffect requer pelo menos uma chave — geralmente Unit para execução única.

O segundo erro é tentar usar LaunchedEffect para inscrição em Flow sem collect. Se você chamar collect em um Flow dentro do LaunchedEffect, a corrotina será suspensa até que o Flow seja concluído (o que no caso do StateFlow nunca acontece), e o bloco de limpeza não poderá terminar corretamente. A abordagem correta é usar collectLatest, que cancela a coleta anterior quando um novo valor chega.

O terceiro erro é passar objetos aninhados como chaves. Se a chave for uma data class com campos mutáveis (var), o LaunchedEffect pode não reconhecer a alteração, pois o Compose usa equals() para comparação, que pode se comportar de forma imprevisível com campos var. Sempre use objetos imutáveis (val) ou primitivos como chaves do LaunchedEffect.

Perguntas frequentes

O que acontece se você não passar uma chave para o LaunchedEffect?

Se você não passar chaves, o LaunchedEffect não compilará — o Kotlin requer pelo menos um argumento para parâmetros vararg. Use LaunchedEffect(Unit) para execução única ao entrar na composição ou passe valores específicos que devem disparar um reinício quando alterados.

O LaunchedEffect pode causar vazamento de memória?

Não, o LaunchedEffect cancela automaticamente a corrotina quando o composable sai da composição, prevenindo vazamentos de memória. No entanto, se a corrotina dentro do LaunchedEffect mantiver uma referência a uma Activity ou Context através de um closure, um vazamento é possível — use viewModelScope para operações de longa duração no ViewModel.

Qual é a diferença entre LaunchedEffect e rememberCoroutineScope?

LaunchedEffect executa uma corrotina automaticamente ao entrar na composição com vinculação de chaves. rememberCoroutineScope fornece um escopo para lançamento manual de corrotinas, por exemplo, em resposta a onItemClick. Use LaunchedEffect para efeitos colaterais automáticos e rememberCoroutineScope para lançar corrotinas com base em eventos do usuário.

Por que o LaunchedEffect é executado várias vezes durante a recomposição?

Se a chave do LaunchedEffect for um tipo instável (por exemplo, var ou uma classe sem equals()), o Compose pode não reconhecer que o valor não mudou e reiniciará o efeito em cada recomposição. Solução: use tipos estáveis (primitivos, strings, data classes com campos val) ou envolva valores mutáveis em remember.

Como parar o LaunchedEffect manualmente?

Não há uma maneira direta de parar o LaunchedEffect externamente — o controle é gerenciado através de chaves. Altere a chave para cancelar a corrotina atual. Se precisar de controle total sobre o ciclo de vida da corrotina, use rememberCoroutineScope com Job e chame manualmente job.cancel() em um evento ou mudança de estado.

Resumo

  • LaunchedEffect — API de efeito colateral no Jetpack Compose para lançar corrotinas assíncronas vinculadas ao ciclo de vida do composable.
  • Chaves — sistema de reinício baseado em chaves: a mudança de chave cancela a corrotina atual e inicia uma nova com parâmetros atualizados.
  • Cancelamento automático — a corrotina é cancelada automaticamente quando o composable sai da composição, prevenindo vazamentos de memória.
  • Carregamento de dados — padrão típico: LaunchedEffect(chave) para carregar dados da rede com tratamento de estados Loading, Success, Error.
  • Debounce — implementado via delay() dentro do LaunchedEffect: se a chave mudar antes do atraso expirar, a corrotina é cancelada.
  • Inscrição Flow — use collectLatest em vez de collect para o tratamento correto de Flow dentro do LaunchedEffect.
  • Chaves estáveis — use apenas tipos imutáveis (val, primitivos, data class) como chaves para comportamento previsível.

Vamos desenvolver um aplicativo móvel chave na mão

A IT Sectr cria aplicativos para iOS e Android para startups e empresas desde 2017. Nós vamos aconselhá-lo e propor a melhor solução.

Discutir o projeto

Leia também