# Kotlin Flow vs StateFlow vs SharedFlow: preguntas de entrevista de Android en 2026 > Las preguntas sobre Kotlin Flow vs StateFlow vs SharedFlow que hacen los entrevistadores de Android en 2026, con respuestas claras, una tabla comparativa y código listo para producción. - Published: 2026-07-01 - Updated: 2026-07-07 - Author: SharpSkill - Tags: android, kotlin, coroutines, flow, interview - Reading time: 9 min --- Kotlin Flow vs StateFlow vs SharedFlow es uno de los temas más frecuentes en las entrevistas de Android en 2026, y confundir los tres es la forma más rápida de perder una ronda de coroutines. StateFlow y SharedFlow son ambos streams calientes construidos sobre Flow, pero existen para resolver problemas distintos: mantener estado frente a difundir eventos. Las preguntas que siguen son las que los entrevistadores de Android hacen de verdad, con respuestas precisas y código listo para producción. > **La respuesta en 20 segundos** > > Un `Flow` es frío y ejecuta su productor una vez por cada colector. `StateFlow` es un stream caliente y conflado que siempre mantiene un único valor actual, ideal para el estado de la UI. `SharedFlow` es un stream caliente sin valor inicial obligatorio, ideal para eventos únicos como una navegación o un snackbar. ## Kotlin Flow vs StateFlow vs SharedFlow: las diferencias fundamentales StateFlow y SharedFlow son especializaciones de `SharedFlow`, que a su vez es un `Flow` caliente. La distinción que los entrevistadores exploran es frío frente a caliente, si se retiene un valor actual y cómo se comporta cada uno ante emisiones duplicadas. Un modelo mental conciso: `Flow` es una receta, `StateFlow` es un único valor mutable y `SharedFlow` es un bus de eventos. | Propiedad | Flow (frío) | StateFlow | SharedFlow | |---|---|---|---| | Temperatura | Frío | Caliente | Caliente | | Valor inicial | Ninguno | Obligatorio | Opcional (vía `replay`) | | Mantiene valor actual | No | Sí, vía `.value` | No | | Emite duplicados | Sí | No (conflado + deduplicado) | Configurable | | Colectores adicionales | Un stream nuevo cada vez | Compartido | Compartido | | Ideal para | Pipelines de datos asíncronos | Estado de la UI | Eventos únicos | La [documentación oficial de coroutines de Kotlin](https://kotlinlang.org/docs/flow.html) considera este comportamiento frío por defecto como la propiedad que define a `Flow`, así que una buena respuesta parte de ahí. ## ¿Por qué un Kotlin Flow es frío por defecto? Un flow frío no hace nada hasta que se llama a `collect()`, y vuelve a ejecutar su bloque productor para cada colector. Dos colectores sobre el mismo flow frío disparan dos llamadas de red independientes. Este es el concepto que más se evalúa, así que un ejemplo breve ancla la respuesta. ```kotlin // SearchRepository.kt fun searchResults(query: String): Flow> = flow { // This block runs fresh for every collector. // Nothing executes until a collector calls collect(). val results = api.search(query) // suspending network call emit(results) // pushed downstream to the collector } ``` Como el productor se reinicia por cada colector, los flows fríos son el valor por defecto correcto para pipelines de datos que deben ejecutarse bajo demanda. Solo se vuelven un problema cuando un valor debe compartirse en toda una pantalla, que es justo donde entran StateFlow y SharedFlow. ## Preguntas de entrevista sobre StateFlow: estado que siempre emite `StateFlow` es un flow caliente que siempre tiene un valor y reenvía ese último valor a cada nuevo colector. Requiere un valor inicial, expone `.value` para lecturas síncronas y confla las emisiones: actualizaciones rápidas y sucesivas pueden saltarse valores intermedios, y asignar el mismo valor dos veces no emite nada porque deduplica por igualdad. Esto lo convierte en el reemplazo moderno de `LiveData` dentro de un `ViewModel`. ```kotlin // SearchViewModel.kt class SearchViewModel(private val repo: SearchRepository) : ViewModel() { // MutableStateFlow demands an initial value, so the UI always has something to render. private val _uiState = MutableStateFlow(SearchUiState.Idle) val uiState: StateFlow = _uiState.asStateFlow() fun onQueryChanged(query: String) { _uiState.value = SearchUiState.Loading viewModelScope.launch { val results = repo.search(query) // update {} applies the change atomically, safe under concurrent callers. _uiState.update { SearchUiState.Success(results) } } } } ``` Una repregunta habitual: ¿por qué exponer `asStateFlow()` en lugar del campo mutable? Le entrega a la UI una vista de solo lectura, de modo que el estado solo puede cambiar a través del ViewModel, lo que mantiene intacto el flujo de datos unidireccional. La propia [guía de StateFlow y SharedFlow de Android](https://developer.android.com/kotlin/flow/stateflow-and-sharedflow) recomienda exactamente este patrón. ## SharedFlow vs StateFlow: elegir el tipo correcto para eventos La pregunta trampa clásica es: «¿puede StateFlow entregar eventos únicos?». La respuesta honesta es no, no de forma segura. Como StateFlow confla y deduplica, un evento de navegación puede perderse ante actualizaciones rápidas, y se vuelve a disparar en un cambio de configuración porque un nuevo colector reproduce el valor retenido. `SharedFlow` con `replay = 0` resuelve ambos casos: no se retiene nada y cada emisión llega únicamente a los colectores activos en el momento de emitir. ```kotlin // CheckoutViewModel.kt class CheckoutViewModel : ViewModel() { // replay = 0: a late collector must NOT re-receive a past navigation event. private val _events = MutableSharedFlow(replay = 0) val events: SharedFlow = _events.asSharedFlow() fun onPaymentConfirmed() { viewModelScope.launch { // emit() suspends if the buffer is full; tryEmit() is the non-suspending variant. _events.emit(CheckoutEvent.NavigateToReceipt) } } } ``` Para preguntas de arquitectura, el razonamiento más profundo detrás de separar el estado de los eventos aparece en la [comparación entre MVVM y MVI](/blog/android/mvvm-vs-mvi-architecture), donde MVI trata los eventos como un stream explícito en lugar de estado mutable. ## Convertir un Flow frío en un StateFlow caliente con stateIn Los entrevistadores preguntan con frecuencia cómo transformar el flow frío de un repositorio en estado de UI. La respuesta es `stateIn` (para un único valor retenido) o `shareIn` (para una difusión sin valor actual). El parámetro `SharingStarted` controla cuándo está activo el upstream, y `WhileSubscribed(5_000)` es la opción estándar porque mantiene vivo el flow durante una rotación sin filtrarlo cuando la pantalla se cierra. ```kotlin // ProfileViewModel.kt val profile: StateFlow = repo.profileStream() .stateIn( scope = viewModelScope, // Keep the upstream alive 5s after the last collector leaves, surviving rotation. started = SharingStarted.WhileSubscribed(5_000), initialValue = Profile.EMPTY ) ``` La diferencia entre `stateIn` y `shareIn` es exactamente la diferencia entre StateFlow y SharedFlow: `stateIn` necesita un `initialValue` y mantiene estado, mientras que `shareIn` recibe un conteo de `replay` y difunde. Repasar el modelo de coroutines en general en [esta guía para dominar las coroutines de Kotlin](/blog/android/mastering-kotlin-coroutines) ayuda a conectar estos operadores con los scopes y la cancelación. ## Recolectar flows de forma segura en el ciclo de vida de Android Una pregunta de nivel senior es cómo recolectar un flow sin filtrar trabajo mientras la pantalla está en segundo plano. Recolectar dentro de un `launch` simple sigue ejecutándose cuando la UI está detenida, desperdiciando CPU y arriesgando crashes. `repeatOnLifecycle(STARTED)` cancela la recolección en `STOP` y la reinicia en `START`. ```kotlin // ProfileFragment.kt (View system) viewLifecycleOwner.lifecycleScope.launch { // Collection restarts on STARTED and cancels on STOPPED: no wasted work in the background. viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) { viewModel.uiState.collect { state -> render(state) } } } ``` En Jetpack Compose el equivalente es `collectAsStateWithLifecycle()`, que deja de recolectar cuando la app pasa a segundo plano y se reanuda al volver. ```kotlin // ProfileScreen.kt (Jetpack Compose) @Composable fun ProfileScreen(viewModel: ProfileViewModel) { // collectAsStateWithLifecycle stops collecting when the app is backgrounded. val state by viewModel.uiState.collectAsStateWithLifecycle() ProfileContent(state) } ``` Esta conciencia del ciclo de vida es la razón por la que StateFlow junto con `collectAsStateWithLifecycle` es el patrón de estado por defecto en las apps modernas con Compose, un punto que se profundiza en estas [preguntas de entrevista sobre Jetpack Compose](/blog/android/jetpack-compose-interview-questions). ## Cómo probar las emisiones de StateFlow y SharedFlow Las pruebas son donde los candidatos senior marcan la diferencia. El enfoque ingenuo lee `stateFlow.value` una sola vez, pero eso omite los estados intermedios y no puede observar un SharedFlow en absoluto. La respuesta estándar menciona la librería Turbine, que suspende hasta que llega cada emisión y hace fallar la prueba si un valor esperado nunca aparece. ```kotlin // SearchViewModelTest.kt @Test fun `emits Loading then Success on query`() = runTest { val viewModel = SearchViewModel(fakeRepo) viewModel.uiState.test { assertEquals(SearchUiState.Idle, awaitItem()) // initial value viewModel.onQueryChanged("kotlin") assertEquals(SearchUiState.Loading, awaitItem()) // intermediate state assertEquals(SearchUiState.Success(fakeResults), awaitItem()) cancelAndIgnoreRemainingEvents() } } ``` Combinar Turbine con `runTest` y un dispatcher de prueba mantiene las aserciones deterministas, y mencionar `StandardTestDispatcher` frente a `UnconfinedTestDispatcher` demuestra experiencia real probando coroutines. ## Repreguntas frecuentes en entrevistas sobre Kotlin Flow Los entrevistadores cierran con preguntas rápidas. ¿Qué hace `SharingStarted.WhileSubscribed(5_000)`? Inicia el upstream con el primer suscriptor y lo detiene 5 segundos después de que el último se da de baja, tiempo suficiente para sobrevivir a una rotación. ¿Por qué StateFlow omite valores duplicados? Compara con `equals()`, así que emitir un valor igual no tiene efecto, y por eso las data classes importan para el estado. ¿Puede SharedFlow comportarse como StateFlow? Poner `replay = 1` hace que retenga el último valor, pero sigue sin tener un `.value` síncrono y nunca deduplica. ¿Qué controla la contrapresión en un SharedFlow? Los parámetros `extraBufferCapacity` y `onBufferOverflow`, siendo `BufferOverflow.DROP_OLDEST` una opción habitual para eventos. Más práctica sobre estos operadores está en el [módulo de entrevista sobre coroutines y Flow de Kotlin](/technologies/android/interview-questions/android-kotlin-coroutines-flow). > **El error que reprueba a los candidatos** > > Usar `StateFlow` para eventos de navegación o snackbar. Como reenvía su último valor a los nuevos colectores, el evento se vuelve a disparar tras una rotación y el usuario navega dos veces. Conviene recurrir a `SharedFlow` con `replay = 0`, o modelar un evento de una sola vez que se limpia después de manejarlo. Vale la pena echar un vistazo al código fuente de kotlinx.coroutines y a su [referencia del paquete flow en GitHub](https://github.com/Kotlin/kotlinx.coroutines/tree/master/kotlinx-coroutines-core) antes de una entrevista, ya que el KDoc de `StateFlow` y `SharedFlow` enuncia con precisión las garantías de conflación y replay. ## Conclusión La distinción entre Kotlin Flow vs StateFlow vs SharedFlow premia el lenguaje preciso en una entrevista. Ideas clave para llevar: - `Flow` es frío: el productor se vuelve a ejecutar para cada colector, por lo que encaja en pipelines de datos bajo demanda. - `StateFlow` es caliente, siempre mantiene un valor y confla y deduplica, lo que lo convierte en la herramienta para el estado de la UI. - `SharedFlow` es caliente y no exige valor inicial, lo que lo hace la opción correcta para eventos únicos. - La conversión de frío a caliente se hace con `stateIn` o `shareIn`, y `SharingStarted.WhileSubscribed(5_000)` permite sobrevivir a una rotación. - Recolectar con `repeatOnLifecycle(STARTED)` o `collectAsStateWithLifecycle()` evita el trabajo en segundo plano y las fugas. - Nunca conviene entregar eventos a través de StateFlow: su comportamiento de replay los vuelve a disparar tras un cambio de configuración. --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/es/blog/android/kotlin-flow-vs-stateflow-vs-sharedflow-interview