A ViewModel holds UI-related data and survives configuration changes (like screen rotation) that would otherwise destroy and recreate an Activity/Fragment, losing in-memory state.

class CounterViewModel : ViewModel() {
    private val _count = MutableLiveData(0)
    val count: LiveData<Int> = _count   // exposed as read-only

    fun increment() {
        _count.value = (_count.value ?: 0) + 1
    }
}
class CounterFragment : Fragment() {
    private val viewModel: CounterViewModel by viewModels()

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        viewModel.count.observe(viewLifecycleOwner) { newCount ->
            binding.countText.text = newCount.toString()
        }
        binding.incrementButton.setOnClickListener { viewModel.increment() }
    }
}

Exposing a private MutableLiveData alongside a public read-only LiveData is a deliberate pattern: the Fragment can observe changes but cannot directly mutate the ViewModel’s state, keeping state changes centralized in one place.

viewLifecycleOwner (not this) matters here — observing with the Fragment’s own lifecycle can cause the observer to fire on a destroyed view.