StateFlow: contenedor de estado activo para la interfaz
Use StateFlow como contenedor de estado reactivo y obsérvelo en ViewModels.
StateFlow: contenedor de estado activo para la interfaz es una lección gratuita de Kotlin Academy en CoddyKit. Esta es la lección 1 de 4. Puedes leer la lección completa abajo gratuitamente — luego la practicas en el navegador con un editor de código integrado y un tutor de IA 24/7. Forma parte de la ruta de aprendizaje de Kotlin Academy, y tu progreso se sincroniza en la web y la app de CoddyKit. El curso de Kotlin Academy incluye 4 lecciones en total.
¿Qué es StateFlow?
StateFlow es un Flow caliente que mantiene el estado, siempre tiene un valor y emite actualizaciones a todos los colectores. Sustituye a LiveData en las arquitecturas modernas de 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 frente a StateFlow
MutableStateFlow es la implementación mutable que se usa internamente. Exponga StateFlow (de solo lectura) a los observadores externos mediante un cast o 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-- }
}Recopilación de StateFlow
Recopile StateFlow como cualquier otro Flow. El valor más reciente se emite inmediatamente al iniciar la recopilación.
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 es conflado
StateFlow solo emite cuando cambia el valor. Si establece el mismo valor dos veces, solo se produce una emisión; combina los valores consecutivos duplicados.
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 en ViewModel
El patrón canónico de ViewModel: la lógica de negocio modifica un MutableStateFlow privado y la interfaz de usuario observa un StateFlow público.
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!")
}
}
}update() para modificaciones atómicas
Use update { } para actualizar atómicamente el estado basándose en el valor actual; es importante para las actualizaciones simultáneas.
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 actualizaciones optimistas
compareAndSet(expect, update) solo establece el nuevo valor si el valor actual coincide con expect. Resulta útil para la concurrencia optimista.
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 frente a LiveData
StateFlow funciona sin el ciclo de vida de Android. LiveData tiene en cuenta el ciclo de vida, pero solo funciona en Android. Prefiera StateFlow para ViewModels compartidos de 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: convertir un Flow frío en StateFlow
flow.stateIn(scope, started, initialValue) convierte un Flow frío en un StateFlow caliente y comparte una única suscripción entre todos los colectores.
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()
}Estrategias de SharingStarted
Eagerly: se inicia inmediatamente. Lazily: se inicia con el primer colector. WhileSubscribed(stopTimeout): se detiene cuando no hay colectores y se reinicia cuando uno se suscribe.
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)Observación del estado derivado
Derive un nuevo StateFlow a partir de uno existente usando map + stateIn. El flujo derivado se actualiza cada vez que cambia el origen.
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()
}Comprobación rápida
¿Qué hace StateFlow cuando se establece el mismo valor dos veces?
Resumen
StateFlow es un Flow caliente, siempre tiene un valor y combina emisiones repetidas. Use MutableStateFlow internamente y exponga StateFlow externamente. Use update() para modificaciones simultáneas y stateIn() para convertir flujos fríos.
Preguntas frecuentes
¿La lección «StateFlow: contenedor de estado activo para la interfaz» es gratis?
Sí — el texto completo de «StateFlow: contenedor de estado activo para la interfaz» es gratis para leer aquí en la web. Para practicarla de forma interactiva (editor de código integrado y tutor de IA 24/7) y desbloquear el resto del curso de Kotlin Academy, actualiza a CoddyKit PRO. El curso de Kotlin Academy incluye 4 lecciones en total.
¿Qué aprenderé en «StateFlow: contenedor de estado activo para la interfaz»?
Use StateFlow como contenedor de estado reactivo y obsérvelo en ViewModels. Practicas Kotlin Academy con código real que ejecutas directamente en el navegador, y un tutor de IA 24/7 responde tus preguntas mientras trabajas en la lección.
¿Necesito experiencia previa para empezar Kotlin Academy?
No se requiere experiencia previa. Kotlin Academy en CoddyKit está estructurado para principiantes hasta estudiantes avanzados, así que puedes empezar aquí o desde el inicio y avanzar a tu ritmo. Esta es la lección 1 de 4.
¿Cuánto tiempo toma la lección «StateFlow: contenedor de estado activo para la interfaz»?
La mayoría de las lecciones de CoddyKit toman alrededor de 5–10 minutos. Cada una es compacta e interactiva, así que avanzas constantemente y retomas exactamente por donde dejaste en la web y la app.
¿Puedo escribir y ejecutar código en esta lección de Kotlin Academy?
Sí. Cada lección de Kotlin Academy incluye un editor de código integrado, así que escribes y ejecutas código real directamente en tu navegador y obtienes retroalimentación instantánea de IA — sin configuración local necesaria.
Todas las lecciones de este curso
- StateFlow: contenedor de estado activo para la interfaz
- SharedFlow: buses de eventos y eventos de una sola emisión
- Convertir Cold Flow en Hot con shareIn y stateIn
- Probar StateFlow y SharedFlow con Turbine