Representing View State with Kotlin Data Classes
ViewModel, we can represent the current state of the UI and pass this to an
Activity which can render it. Using this architecture, an
Activity will react to changes in the view state as they happen.
Since this view state is typically only changed by the
ViewModel, we can make it immutable as far as the
Activity is concerned.
Representing the UI State
We can define the UI state as a Kotlin data class within our
ViewModel; we take this as a convenient place to add default values for each of the fields.
Exposing observable state
Once defined like this, we can expose a
LiveData<ViewState> to which an
Activity can observe changes.
Above, we define the
viewState value which is observable, and we push a default value to it using the
init block. For convenience, we also define a private function that can be used in the
ViewModel to obtain the current view state at any time.
nullable, even though we know it won’t be since we’ve provided a default value upon creating, so this function saves the rest of the class from thinking about
viewState.value from ever being
null. We need to use the ugly
!! syntax here, but it’s isolated to this one function and saves the rest of the view model from needing to handle
Observing UI Changes
Activity we can subscribe to observing any changes to this view state.
Every time a change is pushed from the
Activity immediately gets notified, and renders the UI accordingly.
Making changes to the UI state
So how do changes to the view state happen? Since we’ve defined the view state as being immutable, how do we change anything?
ViewModel wants to make a change that the
Activity will render, it copies the existing view state and changes only the values it needs to, preserving the existing values for the other fields.
The copy() method
To update one or more view state properties, the
ViewModel grabs the current view state, as returned by
currentViewState(), and uses the
copy method to set a new value on one or more fields.
copy method is provided as part of the Kotlin
data class. When we use
copy, all fields in the object remain as their original value unless you provide a new value.
It’s perfectly fine to define multiple fields that have changed in your view state as part of that copy method too, making it a convenient way of capturing changes. For example:
viewState.value = currentViewState().copy(isEditing = hasFocus, showClearButton = false)
LiveData, any time a new value is set using
Activity will immediately be notified that the UI state has changed and it will render the new UI.
For more source code as a reference, you can check out the (work in progress) new DuckDuckGo browser on GitHub.
We frequently create classes whose main purpose is to hold data. In such a class some standard functionality and…kotlinlang.org
A new collection of libraries that help you design robust, testable, and maintainable apps.developer.android.com
ViewModel lets you manage your UI's data in a lifecycle-aware fashion.developer.android.com