StateFlow: contêiner de estado ativo para a interface
Use StateFlow como contêiner reativo de estado e observe-o em ViewModels.
StateFlow: contêiner de estado ativo para a interface é uma aula grátis de Kotlin Academy no CoddyKit. Esta é a aula 1 de 4. Você pode ler a aula completa abaixo gratuitamente — depois pratica ao vivo no navegador com um editor de código integrado e um tutor de IA 24/7. Faz parte do caminho de aprendizado de Kotlin Academy, e seu progresso é sincronizado entre a web e o app CoddyKit. O curso de Kotlin Academy inclui 4 aulas no total.
O Que é StateFlow
StateFlow é um fluxo quente que mantém estado, sempre tem um valor e emite atualizações para todos os coletores. Ele substitui o LiveData nas arquiteturas modernas do Kotlin.
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun main() = runBlocking {
val stateFlow = MutableStateFlow(0) // initial value
println("Current: ${stateFlow.value}")
stateFlow.value = 42
println("Updated: ${stateFlow.value}")
}MutableStateFlow versus StateFlow
MutableStateFlow é a implementação mutável usada internamente. Exponha StateFlow (somente leitura) para observadores externos fazendo uma conversão de tipo ou usando .asStateFlow().
import kotlinx.coroutines.flow.*
class CounterViewModel {
private val _count = MutableStateFlow(0) // mutable internally
val count: StateFlow<Int> = _count.asStateFlow() // read-only externally
fun increment() { _count.value++ }
fun decrement() { _count.value-- }
}Coletando StateFlow
Cole StateFlow como qualquer outro fluxo. O valor mais recente é emitido imediatamente durante a coleta.
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun main() = runBlocking {
val state = MutableStateFlow("loading")
launch {
state.collect { println("State: $it") }
}
delay(50); state.value = "success"
delay(50); state.value = "idle"
delay(50)
coroutineContext.cancelChildren()
}StateFlow é Conflacionado
StateFlow só emite quando o valor muda. Se você definir o mesmo valor duas vezes, ocorrerá apenas uma emissão — valores consecutivos duplicados são mesclados.
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun main() = runBlocking {
val flow = MutableStateFlow("A")
launch {
flow.collect { println("Received: $it") }
}
delay(50)
flow.value = "A" // no emission — same value
flow.value = "B" // emits
flow.value = "B" // no emission — same value
flow.value = "C" // emits
delay(50)
coroutineContext.cancelChildren()
}StateFlow no ViewModel
O padrão canônico de ViewModel: MutableStateFlow privado, alterado pela lógica de negócio, e StateFlow público, observado pela interface do usuário.
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
sealed class UiState { object Loading : UiState(); data class Success(val data: String) : UiState(); data class Error(val msg: String) : UiState() }
class MyViewModel(scope: CoroutineScope) {
private val _uiState = MutableStateFlow<UiState>(UiState.Loading)
val uiState: StateFlow<UiState> = _uiState.asStateFlow()
init {
scope.launch {
delay(100) // simulate load
_uiState.value = UiState.Success("Hello!")
}
}
}Atualização Atômica
Use update { } para atualizar atomicamente o estado com base no valor atual — isso é importante para atualizações concorrentes.
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun main() = runBlocking {
val counter = MutableStateFlow(0)
List(100) {
launch(Dispatchers.Default) {
counter.update { it + 1 } // thread-safe atomic update
}
}.forEach { it.join() }
println("Counter: ${counter.value}") // 100
}compareAndSet para Atualizações Otimistas
compareAndSet(expect, update) só define o novo valor se o valor atual corresponder a expect. É útil para concorrência otimista.
import kotlinx.coroutines.flow.*
fun main() {
val state = MutableStateFlow("idle")
val changed = state.compareAndSet("idle", "loading")
println("Changed: $changed, Value: ${state.value}") // true, loading
val changed2 = state.compareAndSet("idle", "error") // won't change
println("Changed: $changed2, Value: ${state.value}") // false, loading
}StateFlow versus LiveData
StateFlow funciona sem o ciclo de vida do Android. LiveData considera o ciclo de vida, mas funciona somente no Android. Prefira StateFlow para ViewModels compartilhados com KMP.
import kotlinx.coroutines.flow.*
// StateFlow: pure Kotlin, works in commonMain
// val state = MutableStateFlow("value")
// LiveData: Android-only, lifecycle-aware
// val liveData = MutableLiveData("value")
// In Compose, collect StateFlow with:
// val state by viewModel.uiState.collectAsState()
fun main() { println("StateFlow: multiplatform; LiveData: Android-only") }stateIn: Convertendo um Fluxo Frio em StateFlow
flow.stateIn(scope, started, initialValue) converte um fluxo frio em um StateFlow quente, compartilhando uma assinatura entre todos os coletores.
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun main() = runBlocking {
val cold = flow {
println("Fetching...")
delay(100)
emit("data")
}
val hot: StateFlow<String> = cold.stateIn(
scope = this,
started = SharingStarted.Lazily,
initialValue = "loading"
)
launch { hot.collect { println("A: $it") } }
launch { hot.collect { println("B: $it") } }
delay(200)
coroutineContext.cancelChildren()
}Estratégias de SharingStarted
Imediatamente: inicia na hora. De forma preguiçosa: inicia com o primeiro coletor. WhileSubscribed(stopTimeout): para quando não há coletores e reinicia quando um deles se inscreve.
import kotlinx.coroutines.flow.*
// Eagerly: upstream starts right away
// SharingStarted.Eagerly
// Lazily: waits for first subscriber
// SharingStarted.Lazily
// WhileSubscribed: stops 5s after last subscriber
// SharingStarted.WhileSubscribed(5000)
// Typical ViewModel usage:
// val uiState = repo.dataFlow.stateIn(viewModelScope, WhileSubscribed(5000), Loading)Observando Estado Derivado
Derive um novo StateFlow de um existente usando map + stateIn. O fluxo derivado é atualizado sempre que a fonte muda.
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun main() = runBlocking {
val count = MutableStateFlow(5)
val doubled = count.map { it * 2 }.stateIn(this, SharingStarted.Eagerly, 10)
println(doubled.value) // 10
count.value = 7
delay(50)
println(doubled.value) // 14
coroutineContext.cancelChildren()
}Verificação Rápida
O que StateFlow faz quando você define o mesmo valor duas vezes?
Recapitulação
StateFlow é um fluxo quente, sempre com valor e conflacionado. Use MutableStateFlow internamente e exponha StateFlow externamente. Use atualização para alterações concorrentes e stateIn() para converter fluxos frios.
Perguntas Frequentes
A aula “StateFlow: contêiner de estado ativo para a interface” é grátis?
Sim — o texto completo de “StateFlow: contêiner de estado ativo para a interface” é grátis para ler aqui na web. Para praticá-la interativamente (um editor de código integrado e um tutor de IA 24/7) e desbloquear o restante do curso de Kotlin Academy, atualize para CoddyKit PRO. O curso de Kotlin Academy inclui 4 aulas no total.
O que vou aprender em “StateFlow: contêiner de estado ativo para a interface”?
Use StateFlow como contêiner reativo de estado e observe-o em ViewModels. Você pratica Kotlin Academy com código prático que executa diretamente no navegador, e um tutor de IA 24/7 responde suas dúvidas enquanto trabalha na aula.
Preciso ter experiência prévia para começar Kotlin Academy?
Nenhuma experiência prévia é necessária. Kotlin Academy no CoddyKit é estruturado para alunos iniciantes até avançados, então você pode começar aqui ou desde o início e aprender no seu ritmo. Esta é a aula 1 de 4.
Quanto tempo leva a aula “StateFlow: contêiner de estado ativo para a interface”?
A maioria das aulas CoddyKit leva cerca de 5–10 minutos. Cada uma é compacta e interativa, então você faz progresso constante e retoma exatamente de onde parou entre web e app.
Posso escrever e executar código nesta aula de Kotlin Academy?
Sim. Cada aula de Kotlin Academy inclui um editor de código integrado, então você escreve e executa código real direto no navegador e recebe feedback de IA instantaneamente — nenhuma configuração local necessária.
Todas as aulas deste curso
- StateFlow: contêiner de estado ativo para a interface
- SharedFlow: barramentos de eventos e eventos de disparo único
- Convertendo Flow frio em ativo com shareIn e stateIn
- Testando StateFlow e SharedFlow com Turbine