---
title: "ViewModel em Android/Kotlin: O que É e Como Funciona | Kotlin Brasil"
url: "https://kotlin.dev.br/glossario/viewmodel/"
markdown_url: "https://kotlin.dev.br/glossario/viewmodel.MD"
description: "Entenda o que é ViewModel no Android com Kotlin, como gerenciar estado de UI, ciclo de vida, sintaxe com exemplos práticos e erros comuns."
date: "2025-08-22"
author: "Karina Melo"
---

# ViewModel em Android/Kotlin: O que É e Como Funciona | Kotlin Brasil

Entenda o que é ViewModel no Android com Kotlin, como gerenciar estado de UI, ciclo de vida, sintaxe com exemplos práticos e erros comuns.


## O que é ViewModel no Android?

ViewModel é um componente da biblioteca Android Jetpack projetado para armazenar e gerenciar dados relacionados a interface de usuário de forma consciente do ciclo de vida. Quando uma Activity ou Fragment e destruida e recriada (por exemplo, ao girar a tela), o ViewModel sobrevive a essa recriação e mantém os dados intactos, evitando carregamentos desnecessários.

O ViewModel separa a lógica de apresentação da camada de UI. A Activity ou Fragment cuida apenas de exibir dados e capturar interações do usuário, enquanto o ViewModel gerencia o estado, executa operações assíncronas e expõe dados reativos.

## Configuração

Adicione as dependências no `build.gradle.kts`:

```kotlin
dependencies {
    implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.7")
    implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.8.7")
    implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.8.7")

    // Para usar com Compose
    implementation("androidx.activity:activity-compose:1.9.3")
}
```

## Sintaxe básica

Um ViewModel básico herda da classe `ViewModel()`:

```kotlin
import androidx.lifecycle.ViewModel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow

class ContadorViewModel : ViewModel() {

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

    fun incrementar() {
        _contador.value++
    }

    fun decrementar() {
        _contador.value--
    }

    fun resetar() {
        _contador.value = 0
    }
}
```

Na Activity ou Fragment, você obtém o ViewModel assim:

```kotlin
import androidx.activity.viewModels

class ContadorActivity : AppCompatActivity() {

    private val viewModel: ContadorViewModel by viewModels()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        lifecycleScope.launch {
            viewModel.contador.collect { valor ->
                binding.textoContador.text = "Contagem: $valor"
            }
        }

        binding.botaoIncrementar.setOnClickListener {
            viewModel.incrementar()
        }
    }
}
```

## Ciclo de vida do ViewModel

O ViewModel e criado na primeira vez que a Activity ou Fragment solicita e sobrevive a recriacoes de configuração (rotação de tela, mudanca de idioma). Ele só e destruído quando a Activity e finalizada definitivamente (o usuário navega para tras ou chama `finish()`).

```
Activity criada  -->  ViewModel criado
Tela rotacionada -->  Activity destruida e recriada, ViewModel SOBREVIVE
Usuario sai      -->  Activity finalizada, ViewModel.onCleared() chamado
```

O método `onCleared()` e chamado quando o ViewModel e destruído definitivamente, permitindo limpar recursos:

```kotlin
class MinhaViewModel : ViewModel() {

    private val conexao = abrirConexao()

    override fun onCleared() {
        super.onCleared()
        conexao.fechar()
        println("ViewModel destruido, recursos liberados")
    }
}
```

## Exemplos práticos

### ViewModel com chamada de API

```kotlin
data class Usuario(val id: Int, val nome: String, val email: String)

sealed class UiState<out T> {
    data object Carregando : UiState<Nothing>()
    data class Sucesso<T>(val dados: T) : UiState<T>()
    data class Erro(val mensagem: String) : UiState<Nothing>()
}

class UsuarioViewModel(
    private val repositorio: UsuarioRepositorio
) : ViewModel() {

    private val _estado = MutableStateFlow<UiState<List<Usuario>>>(UiState.Carregando)
    val estado: StateFlow<UiState<List<Usuario>>> = _estado.asStateFlow()

    init {
        carregarUsuarios()
    }

    fun carregarUsuarios() {
        viewModelScope.launch {
            _estado.value = UiState.Carregando
            try {
                val usuarios = repositorio.buscarTodos()
                _estado.value = UiState.Sucesso(usuarios)
            } catch (e: Exception) {
                _estado.value = UiState.Erro(e.message ?: "Erro desconhecido")
            }
        }
    }

    fun buscarPorId(id: Int) {
        viewModelScope.launch {
            _estado.value = UiState.Carregando
            try {
                val usuario = repositorio.buscarPorId(id)
                _estado.value = UiState.Sucesso(listOf(usuario))
            } catch (e: Exception) {
                _estado.value = UiState.Erro("Usuario nao encontrado")
            }
        }
    }
}
```

### ViewModel com Jetpack Compose

```kotlin
@Composable
fun TelaUsuarios(
    viewModel: UsuarioViewModel = viewModel()
) {
    val estado by viewModel.estado.collectAsStateWithLifecycle()

    Column(modifier = Modifier.fillMaxSize().padding(16.dp)) {
        Text(
            text = "Usuarios",
            style = MaterialTheme.typography.headlineMedium
        )

        Spacer(modifier = Modifier.height(16.dp))

        when (val uiState = estado) {
            is UiState.Carregando -> {
                CircularProgressIndicator(
                    modifier = Modifier.align(Alignment.CenterHorizontally)
                )
            }
            is UiState.Sucesso -> {
                LazyColumn {
                    items(uiState.dados) { usuario ->
                        Card(
                            modifier = Modifier
                                .fillMaxWidth()
                                .padding(vertical = 4.dp)
                        ) {
                            Column(modifier = Modifier.padding(16.dp)) {
                                Text(text = usuario.nome)
                                Text(
                                    text = usuario.email,
                                    style = MaterialTheme.typography.bodySmall
                                )
                            }
                        }
                    }
                }
            }
            is UiState.Erro -> {
                Text(
                    text = uiState.mensagem,
                    color = MaterialTheme.colorScheme.error
                )
                Button(onClick = { viewModel.carregarUsuarios() }) {
                    Text("Tentar novamente")
                }
            }
        }
    }
}
```

### ViewModel com SavedStateHandle

O `SavedStateHandle` permite que dados do ViewModel sobrevivam até mesmo a morte do processo pelo sistema operacional:

```kotlin
class PesquisaViewModel(
    private val savedStateHandle: SavedStateHandle
) : ViewModel() {

    val termoPesquisa: StateFlow<String> =
        savedStateHandle.getStateFlow("termo_pesquisa", "")

    private val _resultados = MutableStateFlow<List<String>>(emptyList())
    val resultados: StateFlow<List<String>> = _resultados.asStateFlow()

    fun pesquisar(termo: String) {
        savedStateHandle["termo_pesquisa"] = termo
        viewModelScope.launch {
            _resultados.value = executarPesquisa(termo)
        }
    }

    private suspend fun executarPesquisa(termo: String): List<String> {
        // Simula chamada de rede
        kotlinx.coroutines.delay(500)
        return listOf("Resultado 1 para $termo", "Resultado 2 para $termo")
    }
}
```

### ViewModel com injeção de dependência (Hilt)

```kotlin
@HiltViewModel
class ProdutoViewModel @Inject constructor(
    private val repositorio: ProdutoRepositorio,
    private val analytics: AnalyticsService
) : ViewModel() {

    private val _produtos = MutableStateFlow<List<Produto>>(emptyList())
    val produtos: StateFlow<List<Produto>> = _produtos.asStateFlow()

    private val _carrinho = MutableStateFlow<List<Produto>>(emptyList())
    val carrinho: StateFlow<List<Produto>> = _carrinho.asStateFlow()

    init {
        carregarProdutos()
    }

    private fun carregarProdutos() {
        viewModelScope.launch {
            _produtos.value = repositorio.listarTodos()
            analytics.registrarEvento("produtos_carregados")
        }
    }

    fun adicionarAoCarrinho(produto: Produto) {
        _carrinho.value = _carrinho.value + produto
        analytics.registrarEvento("produto_adicionado_carrinho")
    }

    fun removerDoCarrinho(produto: Produto) {
        _carrinho.value = _carrinho.value - produto
    }
}
```

## Quando usar ViewModel

- **Qualquer tela que exibe dados dinâmicos** como listas de API, formularios e dados de banco local.
- **Operações assíncronas** que não devem ser canceladas ao rotacionar a tela, como chamadas de rede ou consultas a banco de dados.
- **Compartilhamento de estado entre Fragments** usando um ViewModel com escopo da Activity.
- **Gerenciamento de estado de UI** mantendo o estado de selecoes, filtros, paginação e navegação.
- **separação de responsabilidades** retirando lógica de negócio da Activity/Fragment.

## Casos de Uso no Mundo Real

1. **Telas de listagem com paginação e filtros**: aplicativos de e-commerce e redes sociais usam ViewModels para gerenciar o estado de listas paginadas, filtros ativos e termos de busca. O ViewModel mantém a posicao da lista e os filtros aplicados mesmo quando o usuário rotaciona a tela, evitando recarregamento desnecessário.

2. **Formularios multi-etapas**: aplicativos bancarios e de cadastro usam ViewModels para armazenar dados preenchidos em formularios de várias etapas. Se o usuário navega entre etapas ou rotaciona a tela, os dados já preenchidos sao preservados no ViewModel sem necessidade de persistencia em disco.

3. **Compartilhamento de estado entre Fragments**: telas com abas (tabs) ou paineis laterais que precisam compartilhar dados usam um ViewModel com escopo da Activity. Quando o usuário seleciona um item em um Fragment, o outro Fragment pode observar a mudanca via o ViewModel compartilhado.

4. **Controle de operações assíncronas em telas de detalhe**: telas que exibem detalhes de um item e permitem acoes como favoritar, compartilhar ou excluir usam o ViewModel para coordenar essas operações. O `viewModelScope` garante que chamadas de rede em andamento não sejam canceladas por mudancas de configuração, mas sejam canceladas quando o usuário sai da tela.

## Boas Praticas

- Nunca passe `Context`, `Activity`, `Fragment` ou `View` para o ViewModel. O ViewModel sobrevive a Activity e manter essas referências causa vazamento de memória. Se precisar de contexto, use `AndroidViewModel` com `Application`.
- Exponha dados usando `StateFlow` imutavel (não `MutableStateFlow`). A Activity ou Fragment deve apenas observar, nunca modificar diretamente o estado. Use o padrão de backing property: `private val _estado` (mutavel) e `val estado` (somente leitura).
- Use `sealed class` ou `sealed interface` para modelar os estados da UI (`Carregando`, `Sucesso`, `Erro`). Isso torna o gerenciamento de estado explicito e o compilador garante que todos os estados sejam tratados no `when`.
- Colete flows respeitando o ciclo de vida usando `collectAsStateWithLifecycle()` no Compose ou `repeatOnLifecycle` em Activities/Fragments. Coletar sem respeitar o ciclo de vida pode causar atualizações de UI com a Activity destruida.
- Injete dependências no ViewModel através do construtor usando Hilt ou outra biblioteca de DI. Isso torna o ViewModel testavel, pois permite substituir dependências reais por mocks nos testes unitarios.

## Perguntas Frequentes

**P: O ViewModel sobrevive a rotação de tela. Ele também sobrevive a morte do processo?**
R: Nao. O ViewModel sobrevive apenas a mudancas de configuração (rotação, mudanca de idioma). Quando o sistema mata o processo para liberar memória, o ViewModel e destruído. Para persistir dados além da morte do processo, use `SavedStateHandle`, que armazena dados no `Bundle` do sistema e os restaura automaticamente.

**P: Posso compartilhar um ViewModel entre Activities diferentes?**
R: Nao diretamente. O ViewModel e vinculado ao ciclo de vida de uma Activity ou Fragment. Para compartilhar entre Fragments, use um ViewModel com escopo da Activity (`activityViewModels()`). Para compartilhar dados entre Activities, use mecanismos como repositórios, banco de dados ou `SharedPreferences`.

**P: Qual a diferenca entre `LiveData` e `StateFlow` no ViewModel?**
R: Ambos servem para expor dados reativos, mas `StateFlow` e parte do Kotlin Coroutines e não depende do Android. `StateFlow` sempre tem um valor inicial, e thread-safe por padrão e se integra melhor com Compose via `collectAsStateWithLifecycle()`. `LiveData` e lifecycle-aware nativamente mas esta sendo gradualmente substituido por `StateFlow` nas recomendacoes oficiais do Google.

**P: Devo colocar lógica de negócio no ViewModel?**
R: O ViewModel deve conter lógica de apresentação (como formatar dados para a UI, gerenciar estado de tela), mas lógica de negócio pura deve ficar em camadas inferiores como use cases ou repositórios. Isso segue o princípio de separação de responsabilidades e facilita a reutilização e testabilidade da lógica de negócio.

## Erros comuns

### Passar Context ou View para o ViewModel

```kotlin
// ERRADO: pode causar vazamento de memoria
class MinhaViewModel(
    private val context: Context // NUNCA faca isso
) : ViewModel()

// CORRETO: use AndroidViewModel se precisar de Application context
class MinhaViewModel(
    application: Application
) : AndroidViewModel(application) {
    val context: Context get() = getApplication()
}
```

O ViewModel sobrevive a Activity. Se ele mantiver uma referência ao Context da Activity, essa Activity não sera coletada pelo garbage collector, causando vazamento de memória.

### Observar dados fora do ciclo de vida

```kotlin
// ERRADO: collect sem respeitar ciclo de vida
viewModel.estado.collect { /* pode atualizar UI com Activity destruida */ }

// CORRETO: usar collectAsStateWithLifecycle no Compose
val estado by viewModel.estado.collectAsStateWithLifecycle()

// CORRETO: usar repeatOnLifecycle em Activities
lifecycleScope.launch {
    repeatOnLifecycle(Lifecycle.State.STARTED) {
        viewModel.estado.collect { estado ->
            atualizarUI(estado)
        }
    }
}
```

### Criar ViewModel manualmente

```kotlin
// ERRADO: perde a associacao com o ciclo de vida
val viewModel = MinhaViewModel()

// CORRETO: usar delegate ou ViewModelProvider
val viewModel: MinhaViewModel by viewModels()
```

## Termos relacionados

- **LiveData**: classe observavel que respeita o ciclo de vida, alternativa mais antiga ao StateFlow para exposicao de dados no ViewModel.
- **StateFlow**: fluxo de estado do Kotlin Coroutines, recomendado atualmente para expor dados reativos no ViewModel.
- **viewModelScope**: CoroutineScope vinculado ao ciclo de vida do ViewModel, cancelado automaticamente em onCleared.
- **SavedStateHandle**: mecanismo para persistir estado do ViewModel entre reconstrucoes do processo.
- **Hilt**: biblioteca de injeção de dependência do Android que simplifica a criação de ViewModels com dependências.
- **Repository pattern**: padrão arquitetural que abstrai fontes de dados, frequentemente usado em conjunto com ViewModels.

## Conclusão

O ViewModel e a peça central da arquitetura moderna de aplicativos Android com Kotlin. Ele resolve problemas fundamentais como perda de estado ao rotacionar a tela, vazamento de memória e acoplamento entre lógica de negócio e UI. Combinado com StateFlow, Compose e injeção de dependência, o ViewModel permite construir aplicativos reativos, testáveis e manteniveis. Dominar esse componente e essencial para qualquer desenvolvedor Android.
