0
0
Android Kotlinmobile~15 mins

State in ViewModel in Android Kotlin - Deep Dive

Choose your learning style9 modes available
Overview - State in ViewModel
What is it?
State in ViewModel means keeping track of data that the app's screen needs to show or use. The ViewModel holds this data safely even if the screen changes or the device rotates. It helps the app remember what the user was doing without losing information. This makes the app feel smooth and reliable.
Why it matters
Without state in ViewModel, the app would lose all its data every time the screen changes or the device rotates. This would make users frustrated because they would have to start over or wait for data to reload. State in ViewModel solves this by keeping data alive and ready, making apps faster and easier to use.
Where it fits
Before learning state in ViewModel, you should understand basic Android app structure and what ViewModel is. After this, you can learn about LiveData or StateFlow to observe state changes and how to connect ViewModel state with UI components like Jetpack Compose or XML layouts.
Mental Model
Core Idea
ViewModel holds and manages UI data so it survives configuration changes and keeps the app state consistent.
Think of it like...
Think of ViewModel as a backpack you carry on a trip. No matter where you go or how many times you change buses (screens), your backpack keeps your important things safe and ready to use.
┌───────────────┐
│   UI Screen   │
└──────┬────────┘
       │
       ▼
┌───────────────┐
│   ViewModel   │
│  (holds state)│
└──────┬────────┘
       │
       ▼
┌───────────────┐
│  Data Source  │
└───────────────┘
Build-Up - 7 Steps
1
FoundationWhat is ViewModel State
🤔
Concept: Introduce the idea that ViewModel stores data for the UI to keep it safe during screen changes.
In Android, the ViewModel is a special class that holds data for the screen. This data is called 'state'. When the screen rotates or changes, the ViewModel keeps this state so the app doesn't lose it. For example, if a user types text in a form, the ViewModel remembers it.
Result
The app keeps user data even if the screen rotates or the user navigates away and back.
Understanding that ViewModel holds state helps you build apps that don’t lose data during common device changes.
2
FoundationWhy State Survives Configuration Changes
🤔
Concept: Explain how ViewModel lifecycle is tied to the screen lifecycle but survives configuration changes.
Normally, when the screen rotates, Android destroys and recreates it. This means all data in the screen is lost. But ViewModel is designed to survive these changes. It stays alive as long as the screen is alive, even if recreated. This is why state in ViewModel is safe.
Result
Data stored in ViewModel remains intact across screen rotations.
Knowing ViewModel lifecycle helps you trust it to keep your UI data safe during device changes.
3
IntermediateUsing MutableState in ViewModel
🤔Before reading on: do you think MutableState automatically updates the UI when changed? Commit to your answer.
Concept: Introduce MutableState as a way to hold state that the UI can observe and react to.
In Kotlin with Jetpack Compose, MutableState holds a value that can change. When you update it, the UI automatically redraws to show the new value. In ViewModel, you create a MutableState variable to hold your data. For example: class MyViewModel : ViewModel() { var count by mutableStateOf(0) private set fun increment() { count++ } } The UI reads count and updates when it changes.
Result
UI components automatically update when ViewModel's MutableState changes.
Understanding MutableState in ViewModel bridges data and UI, making apps reactive and smooth.
4
IntermediateExposing State Safely from ViewModel
🤔Before reading on: should UI be able to change ViewModel state directly? Commit to yes or no.
Concept: Teach how to expose state as read-only to UI to prevent accidental changes.
It's best to keep state variables private in ViewModel and expose them as read-only to UI. For example: private val _text = mutableStateOf("") val text: State = _text fun updateText(newText: String) { _text.value = newText } This way, UI can read text but only ViewModel can change it.
Result
UI can observe state but cannot modify it directly, keeping data safe and controlled.
Knowing how to expose state safely prevents bugs and keeps app logic clean.
5
IntermediateStateFlow as Alternative State Holder
🤔Before reading on: do you think StateFlow is similar to MutableState? Commit to yes or no.
Concept: Introduce StateFlow as a Kotlin tool to hold and emit state changes in ViewModel.
StateFlow is a Kotlin Flow that holds a state and emits updates. It works well with coroutines and can be observed by UI. Example: private val _count = MutableStateFlow(0) val count: StateFlow = _count fun increment() { _count.value++ } UI collects count and updates when it changes.
Result
UI reacts to state changes emitted by StateFlow in ViewModel.
Understanding StateFlow expands your options for reactive state management in ViewModel.
6
AdvancedHandling Complex State in ViewModel
🤔Before reading on: do you think storing multiple related values separately is better than one combined state? Commit to your answer.
Concept: Teach how to manage complex UI state as one data class to keep state consistent.
Instead of many separate state variables, use one data class to hold all UI state. For example: data class UiState(val count: Int = 0, val name: String = "") private val _uiState = mutableStateOf(UiState()) val uiState: State = _uiState fun updateName(newName: String) { _uiState.value = _uiState.value.copy(name = newName) } This keeps state changes atomic and easier to manage.
Result
UI state updates are consistent and easier to track as one object.
Knowing to use a single state object prevents bugs from partial updates and keeps UI in sync.
7
ExpertAvoiding State Loss with SavedStateHandle
🤔Before reading on: do you think ViewModel state survives app process death? Commit yes or no.
Concept: Explain how to use SavedStateHandle to save state across app kills and restores.
ViewModel state survives screen changes but not app process death (like low memory). To save state across process death, use SavedStateHandle: class MyViewModel(savedStateHandle: SavedStateHandle) : ViewModel() { val count = savedStateHandle.getLiveData("count", 0) fun increment() { count.value = (count.value ?: 0) + 1 } } SavedStateHandle stores data in a bundle that Android restores if the app restarts.
Result
State is preserved even if the app is killed and restarted by the system.
Understanding SavedStateHandle helps build robust apps that don’t lose user data unexpectedly.
Under the Hood
ViewModel is tied to the lifecycle of a UI controller like Activity or Fragment but is not destroyed on configuration changes. It lives in memory as long as the UI controller is alive. MutableState and StateFlow hold data and notify observers when data changes, triggering UI updates. SavedStateHandle uses a Bundle to save key-value pairs that Android can restore after process death.
Why designed this way?
Android apps often lose UI data on rotation or process death, frustrating users. ViewModel was designed to separate UI data from UI controllers to survive configuration changes. MutableState and StateFlow provide reactive data holders for modern UI frameworks like Jetpack Compose. SavedStateHandle was added to handle the harder case of process death, balancing simplicity and reliability.
┌───────────────┐
│   Activity    │
│  (UI Layer)   │
└──────┬────────┘
       │
       ▼
┌───────────────┐
│   ViewModel   │
│ (holds state) │
│  MutableState │
│  StateFlow    │
│  SavedState   │
└──────┬────────┘
       │
       ▼
┌───────────────┐
│  Data Source  │
└───────────────┘
Myth Busters - 4 Common Misconceptions
Quick: Does ViewModel state survive app process death? Commit yes or no.
Common Belief:ViewModel state always survives everything, including app kills.
Tap to reveal reality
Reality:ViewModel state survives configuration changes but is lost if the app process is killed by the system.
Why it matters:Assuming ViewModel state survives app kills leads to lost user data and bad user experience.
Quick: Can UI components safely modify ViewModel state variables directly? Commit yes or no.
Common Belief:UI can and should change ViewModel state variables directly for simplicity.
Tap to reveal reality
Reality:State variables should be private or read-only to UI; only ViewModel should modify them to keep control and avoid bugs.
Why it matters:Allowing UI to change state directly can cause inconsistent data and hard-to-find bugs.
Quick: Does using multiple separate state variables always make state management easier? Commit yes or no.
Common Belief:Splitting state into many variables is simpler and clearer.
Tap to reveal reality
Reality:Using one combined state object is often better to keep state consistent and avoid partial updates.
Why it matters:Multiple separate states can cause UI to show outdated or mismatched data.
Quick: Is MutableState the only way to hold state in ViewModel? Commit yes or no.
Common Belief:MutableState is the only or best way to hold state in ViewModel.
Tap to reveal reality
Reality:StateFlow and LiveData are also common and sometimes better choices depending on app needs.
Why it matters:Limiting to one state holder can reduce flexibility and app performance.
Expert Zone
1
MutableState triggers recomposition only when the value actually changes, preventing unnecessary UI updates.
2
SavedStateHandle integrates with ViewModel but requires careful key management to avoid conflicts and data loss.
3
StateFlow supports coroutines and can be combined with other flows for complex reactive data streams.
When NOT to use
Avoid storing large or complex data like images or database cursors in ViewModel state; use repositories or caches instead. For very simple apps, using only LiveData might be simpler. When app needs to share state across multiple screens, consider shared ViewModels or other state management solutions.
Production Patterns
In production, ViewModel state is often combined with repository patterns to fetch and cache data. State is exposed as immutable to UI, and events are handled via functions. SavedStateHandle is used for critical UI state that must survive process death. Complex UI states use data classes and sealed classes for clarity.
Connections
Redux State Management
Both manage UI state in a centralized, predictable way.
Understanding ViewModel state helps grasp Redux’s idea of a single source of truth for UI data.
Functional Reactive Programming
ViewModel state with StateFlow or MutableState follows reactive programming principles.
Knowing reactive streams helps understand how UI updates automatically when state changes.
Human Memory and Attention
ViewModel state acts like short-term memory that keeps important info during distractions (screen changes).
Seeing ViewModel as memory helps appreciate why preserving state improves user experience.
Common Pitfalls
#1Losing state on screen rotation
Wrong approach:class MyActivity : AppCompatActivity() { var count = 0 override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) count++ println("Count: $count") } }
Correct approach:class MyViewModel : ViewModel() { var count by mutableStateOf(0) private set fun increment() { count++ } } class MyActivity : AppCompatActivity() { private val viewModel: MyViewModel by viewModels() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) viewModel.increment() println("Count: ${viewModel.count}") } }
Root cause:Storing state in Activity causes data loss because Activity is recreated on rotation.
#2Allowing UI to modify ViewModel state directly
Wrong approach:class MyViewModel : ViewModel() { var text by mutableStateOf("") } // In UI viewModel.text = "new value"
Correct approach:class MyViewModel : ViewModel() { private var _text = mutableStateOf("") val text: State = _text fun updateText(newText: String) { _text.value = newText } } // In UI viewModel.updateText("new value")
Root cause:Exposing mutable state publicly allows uncontrolled changes and bugs.
#3Not using SavedStateHandle for critical state
Wrong approach:class MyViewModel : ViewModel() { var count by mutableStateOf(0) fun increment() { count++ } }
Correct approach:class MyViewModel(savedStateHandle: SavedStateHandle) : ViewModel() { val count = savedStateHandle.getLiveData("count", 0) fun increment() { count.value = (count.value ?: 0) + 1 } }
Root cause:Ignoring process death causes loss of important UI state.
Key Takeaways
ViewModel holds UI state safely across configuration changes like screen rotations.
State should be exposed as read-only to UI to keep control and avoid bugs.
MutableState and StateFlow are modern tools to hold reactive state in ViewModel.
SavedStateHandle helps preserve state even if the app process is killed and restarted.
Using a single data class for complex state keeps UI consistent and easier to manage.