What is a ViewModel?
Introduction
A ViewModel is like a manager for your app's data. It keeps track of information your screen needs, even if the user rotates their phone or leaves and comes back. In Jetpack Compose (and Android in general), ViewModels help you keep your UI and your app's logic separate, making your code easier to understand and maintain.
When to Use a ViewModel
- When you need to keep data around as the user navigates or rotates the device
- When you want to separate your UI code from your business logic
- When you have data that is shared between multiple composables or screens
Main Concepts
- Lifecycle-aware: ViewModels survive configuration changes (like screen rotation) so your data isn't lost.
- UI separation: The ViewModel holds the data and logic, while your composables just display it.
- State holder: The ViewModel is the "single source of truth" for your screen's state.
Dependencies
You will have to add some dependencies to your gradle file for view models to work.
Gradle (build.gradle.kts Module :app)
dependencies {
// ... your existing dependencies ...
implementation(libs.androidx.lifecycle.viewmodel.compose)
// ... rest of your dependencies ...
}
libs.versions.toml
[versions]
# ... your existing versions ...
lifecycle = "2.7.0" # Add this line
[libraries]
androidx-lifecycle-viewmodel-compose = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-compose", version.ref = "lifecycle" }
Creating a New Class File
You will need to create a new class file for your ViewModel.
Right click on your package name, it will start with com.
Then click New and then Kotlin File/Class
In the dialog box select Class and enter the file name "MainViewModel". NOTE: The file name can be anything you want.
That will create a class file and place it in the same package as your MainActivity.kt file.
Creating the ViewModel
You have to have a file that is your view model. For this example I will create a file called MainViewModel.kt. It contains the following code:
import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.ViewModel
class MainViewModel : ViewModel() {
private var _count = mutableStateOf(0)
var count: Int
get() = _count.value
private set(value) {
_count.value = value
}
fun increment() {
count = count + 1
}
}
This Kotlin code defines a MainViewModel class, which extends ViewModel.
The ViewModel class is part of the Android Architecture Components and is designed to store and manage UI-related data in a lifecycle-conscious way.
This means the data within the ViewModel persists across configuration changes (like screen rotations) and the ViewModel itself lives as long as the scope of the UI (e.g., an Activity or Fragment).
Key components:
private var _count = mutableStateOf(0): This declares a private mutable state variable named_countand initializes it to 0.mutableStateOfis a function from the Jetpack Compose UI toolkit that creates an observable state holder. When the value of_countchanges, any UI elements observing it will automatically recompose (update). By making it private, external classes cannot directly modify this internal state.var count: Int: This declares a public read-only property namedcountof typeInt. It has a custom getter and a private setter:get() = _count.value: The getter simply returns the current value of the private_count.private set(value) { _count.value = value }: The setter is private, meaning that while thecountcan be read publicly, it can only be modified internally within theMainViewModelclass. This enforces a pattern where the UI can observe the state, but cannot directly change it; changes must happen through defined functions in the ViewModel.
fun increment(): This is a public function that provides a way to modify thecount. When called, it increments thecountby 1. This function serves as the controlled entry point for the UI to request a state change.
In summary, this ViewModel manages a simple counter.
The UI can observe the count property to display its current value, and it can call the increment() function to request an update to the counter, without directly manipulating the state.
MainActivity.kt
You also must have a mainactivity.kt file. The MainActivity.kt file is the UI that usess the view model for its data. The partial code is shown below:
import androidx.lifecycle.viewmodel.compose.viewModel //this imports the viewModel file
...
//this is the composable function that uses the view model
@Composable
fun CounterScreen() {
// Get or create a ViewModel instance
// viewModel() is a composable function that handles ViewModel lifecycle
val viewModel: MainViewModel = viewModel()
// Column arranges its children vertically
Column(
modifier = Modifier
.padding(top = 50.dp) // Add top padding for better spacing from status bar
.padding(16.dp) // Add padding around all sides
) {
// Button that triggers the increment function in ViewModel
Button(onClick = { viewModel.increment() }) {
Text("Increment")
}
// Text composable that displays the current count
// It automatically updates when the count changes in ViewModel
Text("Count: ${viewModel.count}")
}
}
The key line in the code above is Button(onClick = { viewModel.increment() }) { This is the button that triggers the increment function in the ViewModel.
How this example renders
Above is just a snippet of the code to view the full code, you need to go to my GitHub page and look at the chapter10 mainviewmodel1.kt file and chapter10 mainactivity1.kt file.
Tips for Success
- Use ViewModels for any data you want to keep when the screen changes.
- Keep your UI code (composables) simple—let the ViewModel handle the logic.
- Don't put Android Context or UI elements in your ViewModel.
Common Mistakes to Avoid
- Storing UI elements or Context in the ViewModel (it should only hold data and logic).
- Trying to use a ViewModel for data that should only exist temporarily (like a text field's current value).
- Forgetting to use
by mutableStateOffor state you want Compose to react to.
Best Practices
- Use one ViewModel per screen or feature.
- Expose only the data and functions your UI needs (keep things
privatewhen possible). - Document what your ViewModel does and what state it holds.