Let's think about it this way for a second
MVVM (Model-View-ViewModel) is an architecture pattern that separates the UI (View/Composable) from the business logic (ViewModel) — Model is the data (database, API), View is the UI (Composable), and ViewModel is the middleman between the two (fetching/transforming data and exposing it to the View). The `ViewModel` class survives Activity/screen rotation (an Advanced-level concept) — unlike a Composable's local state (`remember`), a ViewModel's data sticks around through configuration changes.
Let's connect this to a real scenario
If you write `TodoViewModel : ViewModel() { val todos = mutableStateListOf<TodoItem>(); fun addTodo(title: String) { todos.add(TodoItem(...)) } }` — in a Composable, grab the instance with `val viewModel: TodoViewModel = viewModel()` and display `viewModel.todos` in a `LazyColumn` — a button click just needs to call `viewModel.addTodo(...)`, no business logic mixed into the UI code anymore.
Let's walk through it together
class TodoViewModel : ViewModel() {
private val _todos = mutableStateListOf<TodoItem>()
val todos: List<TodoItem> = _todos
fun addTodo(title: String) {
_todos.add(TodoItem(id = UUID.randomUUID().toString(), title = title))
}
}
@Composable
fun TodoScreen(viewModel: TodoViewModel = viewModel()) {
LazyColumn {
items(viewModel.todos) { todo -> Text(todo.title) }
}
}Even after rotating the screen, you should see the todos list in the ViewModel stick around instead of disappearing.5-minute try-it
Write `TodoViewModel` yourself and add 2-3 todo items using `addTodo()` — rotate the emulator (Ctrl+F11) and confirm the todo list doesn't disappear.
A quick word of caution
Avoid exposing state from a ViewModel as a mutable public property (`var todos = mutableListOf<...>()`) — this lets the View modify the state directly, so you should use `private set` or an immutable exposure pattern instead.