ViewModel.
UI state needs to survive configuration changes. It needs a home outside the Activity or Composable. ViewModel provides that home. Today we understand how ViewModel works and how to use it effectively.
Configuration changes destroy Activities.
Rotate the phone, the Activity is destroyed and recreated. Your data, gone. User was filling a form, text lost.
You can save state in bundles, but that is limited. Only small data. Only primitives and parcelables.
UI-related data needs to survive. Loading states, fetched lists, user inputs. But it should not persist forever.
ViewModel holds UI state that survives configuration changes but is cleared when the user leaves the screen.
A ViewModel outlives its Activity or Fragment.
Create an Activity, get a ViewModel. Rotate, Activity dies, new Activity gets the same ViewModel.
The ViewModel lives as long as its scope. For an Activity, until finish is called or back is pressed.
When the scope ends, onCleared is called. Clean up resources there.
The ViewModel is not destroyed on configuration changes. It holds state across rotations, language changes, dark mode switches.
Extend the ViewModel class.
class MyViewModel colon ViewModel. Add properties to hold state. Add functions to modify state.
In Activity or Fragment, get the ViewModel with viewModels.
private val viewModel colon MyViewModel by viewModels.
This provides the same instance across configuration changes. First access creates. Subsequent accesses return existing.
In Compose, use viewModel function.
at Composable fun MyScreen viewModel colon MyViewModel equals viewModel. Gets or creates the ViewModel.
The ViewModel is scoped to the navigation destination or Activity. Same ViewModel across recompositions.
Access state from ViewModel. Call functions on ViewModel. The composable reacts to state changes.
val uiState by viewModel dot uiState dot collectAsState. Collect Flow as Compose state.
Use StateFlow to expose state from ViewModel.
private val underscore uiState equals MutableStateFlow initial value. Mutable for internal updates.
val uiState colon StateFlow equals underscore uiState. Expose as read-only.
Update with underscore uiState dot value equals newState. Or use update function.
StateFlow is perfect for UI state. It always has a value. It emits to new collectors immediately. It works with Compose.
ViewModel provides viewModelScope.
viewModelScope is a CoroutineScope tied to the ViewModel lifecycle.
Launch coroutines in viewModelScope. They cancel automatically when ViewModel is cleared.
viewModelScope dot launch brace fetch data close brace. Safe. Lifecycle-aware.
Use viewModelScope for network calls, database operations, any async work triggered by the UI.
No manual cancellation needed. When the user leaves, coroutines cancel.
ViewModel receives user actions and updates state.
User clicks a button. Composable calls viewModel dot onButtonClick.
ViewModel handles the action. Fetches data, updates state.
State changes. Flow emits. Composable recomposes with new state.
This is unidirectional data flow. Actions go up. State flows down. Clear, predictable.
A counter ViewModel.
class CounterViewModel colon ViewModel brace private val underscore count equals MutableStateFlow 0 val count equals underscore count fun increment brace underscore count dot value plus plus close brace close brace.
Composable: val count by viewModel dot count dot collectAsState. Text "Count colon count". Button onClick viewModel dot increment.
ViewModel holds state. Composable displays. Actions modify state. UI reflects changes.
Survives rotation. Cleared when screen exits.
Let us summarize.
ViewModel holds UI state that survives configuration changes.
Its lifecycle outlives Activities. It is cleared when the scope ends.
Use StateFlow to expose observable state.
Use viewModelScope for lifecycle-aware coroutines.
In Compose, use viewModel function to get the ViewModel.
ViewModel enables clean separation between UI and state management.