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 | StateFlow | SharedFlow | |
|---|---|---|---|
| Holds a value | Yes — but nullable | Yes, always — needs an initial value | No — it's a stream of events |
| Lifecycle-aware | Built-in, zero setup | Via collectAsStateWithLifecycle | Via repeatOnLifecycle |
| Platform | Android only | Pure Kotlin — works in KMP | Pure Kotlin — works in KMP |
| Operators | Few (map, switchMap) | Full coroutine toolbox | Full coroutine toolbox |
| Null safety | Nullable by default | Non-null, enforced by the type | Your choice |
| Repeated values | Re-emits duplicates | Conflates — skips duplicates | Configurable (replay / buffer) |
| Best for | Legacy · Java interop · simple state | The UI state your screen renders | One-time events: navigate, toast |
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.