JetpackCompose1.5EssentialsPreview - Optimized 139 143
JetpackCompose1.5EssentialsPreview - Optimized 139 143
Figure 45-1
This separation of responsibility addresses the issues relating to the lifecycle of activities. Regardless of how
many times an activity is recreated during the lifecycle of an app, the ViewModel instances remain in memory
thereby maintaining data consistency. A ViewModel used by an activity, for example, will remain in memory
until the activity finishes which, in the single activity app, is not until the app exits.
In addition to using ViewModels, the code responsible for gathering data from data sources such as web services
or databases should be built into a separate repository module instead of being bundled with the view model.
This topic will be covered in detail beginning with the chapter entitled “Room Databases and Compose”.
}
With some data encapsulated in the model, the next step is to add a function that can be called from within the
UI to change the counter value:
404
Working with ViewModels in Compose
class MyViewModel : ViewModel() {
fun increaseCount() {
customerCount++
}
}
Even complex models are nothing more than a continuation of these two basic state and function building
blocks.
@Composable
fun TopLevel(model: MyViewModel = viewModel()) {
MainScreen(model.customerCount) { model.increaseCount() }
}
@Composable
fun MainScreen(count: Int, addCount: () -> Unit = {}) {
Column(horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier.fillMaxWidth()) {
Text("Total customers = $count",
Modifier.padding(10.dp))
Button(
onClick = addCount,
) {
Text(text = "Add a Customer")
405
Working with ViewModels in Compose
}
}
}
In the above example, the first function call is made by the onCreate() method to the TopLevel composable
which is declared with a default ViewModel parameter initialized via a call to the viewModel() function:
@Composable
fun TopLevel(model: MyViewModel = viewModel()) {
.
.
The viewModel() function is provided by the Compose view model lifecycle library which needs to be added to
the project’s build dependencies when working with view models as follows:
dependencies {
.
.
implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.6.1")
.
.
If an instance of the view model has already been created within the current scope, the viewModel() function will
return a reference to that instance. Otherwise, a new view model instance will be created and returned.
With access to the ViewModel instance, the TopLevel function is then able to obtain references to the view model
customerCount state variable and increaseCount() function which it passes to the MainScreen composable:
MainScreen(model.customerCount) { model.increaseCount() }
As implemented, Button clicks will result in calls to the view model increaseCount() function which, in turn,
increments the customerCount state. This change in state triggers a recomposition of the user interface, resulting
in the new customer count value appearing in the Text composable.
The use of state and view models will be demonstrated in the chapter entitled “A Compose ViewModel Tutorial”.
Note that new values must be assigned to the live data variable via the value property.
406
Working with ViewModels in Compose
Once we have access to a view model instance, the next step is to make the live data observable. This is achieved
by calling the observeAsState() method on the live data object:
@Composable
fun TopLevel(model: MyViewModel = viewModel()) {
var customerName: String by model.customerName.observeAsState("")
}
In the above code, the observeAsState() call converts the live data value into a state instance and assigns it to
the customerName variable. Once converted, the state will behave in the same way as any other state object,
including triggering recompositions whenever the underlying value changes.
The use of LiveData and view models will be demonstrated in the chapter entitled “A Compose Room Database
and Repository Tutorial”.
45.9 Summary
Until recently, Google has tended not to recommend any particular approach to structuring an Android app.
That changed with the introduction of Android Jetpack which consists of a set of tools, components, libraries,
and architecture guidelines. These architectural guidelines recommend that an app project be divided into
separate modules, each being responsible for a particular area of functionality, otherwise known as “separation
of concerns”. In particular, the guidelines recommend separating the view data model of an app from the code
responsible for handling the user interface. This is achieved using the ViewModel component. In this chapter,
we have covered ViewModel-based architecture and demonstrated how this is implemented when developing
with Compose. We have also explored how to observe and access view model data from within an activity using
both state and LiveData.
407