← Playground
Android · Kotlin

LiveData vs StateFlow — the honest line

"LiveData is dead, use StateFlow" is the interview answer. The real answer isn't about which is newer — it's about what you're modelling: state, or an event. Here's when each is the right call.

Tap any row to see what it really means — and the gotcha behind it.

LiveData
the veteran
StateFlow
the default
SharedFlow
for events
Holds a valueYes — but nullableYes, always — needs an initial valueNo — it's a stream of events
Lifecycle-awareBuilt-in, zero setupVia collectAsStateWithLifecycleVia repeatOnLifecycle
PlatformAndroid onlyPure Kotlin — works in KMPPure Kotlin — works in KMP
OperatorsFew (map, switchMap)Full coroutine toolboxFull coroutine toolbox
Null safetyNullable by defaultNon-null, enforced by the typeYour choice
Repeated valuesRe-emits duplicatesConflates — skips duplicatesConfigurable (replay / buffer)
Best forLegacy · Java interop · simple stateThe UI state your screen rendersOne-time events: navigate, toast
The decision line
Modelling STATE the screen renders?

→ StateFlow

It always has a value, so your UI always has something to draw.

Firing a one-TIME event?

→ SharedFlow

Navigation, a toast, a snackbar — things you fire once, not hold.

Legacy code or Java interop?

→ LiveData is still fine

It's older, not broken. Simple one-shot state is a perfectly good fit.


The mistake that isn't "picking LiveData"

The classic bug isn't choosing the old tool — it's using a state holder (LiveData or StateFlow) for a one-time event. Because state is re-delivered to every new subscriber, a screen rotation re-reads the last value and your app navigates again, or shows the snackbar twice. Events belong in a SharedFlow (or a Channel), which delivers once and doesn't replay. Get the state-vs-event split right and the LiveData-vs-StateFlow debate mostly answers itself.

More like this

Coroutine dispatchers and the Android lifecycle each have an interactive game — play them and they stick.