I'm building a Jetpack Compose Deskop app that currently as a draft looks like this:
The 1
and 2
are two ViewModel
s:
// 1
class FilterViewModel {
val query = mutableStateOf<String?>(null)
val timeframe = mutableStateOf("Any")
}
// 2
class GridViewModel {
val items = mutableListOf("foo", "bar", "baz")
}
I'd like the GridViewModel
to update its items
when the FilterViewModel
changes. How do I do that in this situation? Does Jetpack Compose Deskop have anything to offer here or perhaps there are some more general solutions?
Consider defining a data layer, such as a repository combining a remote and/or local data sources. From the docs linked:
While the UI layer contains UI-related state and UI logic, the data layer contains application data and business logic.
The idea here is that yes you need local data like "Number" both in the query/search from FilterViewModel
and also show the results with "Number" coming through in the items in the list. HOWEVER, with a data layer and data driven architecture they can both be pieces of live data in the ViewModels that observe the same single source of truth in the data layer. General hierarchy:
Example of Hierarchy:
FilterViewModel
reads and write <-> NumbersRepo
, TimeframeRepo
, QueryRepo
FilterViewModel
writes -> CurrentItemsRepo
GridViewModel
reads -> CurrentItemsRepo
These repos and use remote data + Room and DataStore, or they can just be singletons/objects to get started. I would recommend looking at Flow and LiveData for this.
Let's take a look in more detail what this looks like with this Room with a View codelab that uses Words instead of Numbers:
class WordRepository(private val wordDao: WordDao) {
// Observed Flow will notify the observer when the data has changed.
val allWords: Flow<List<Word>> = wordDao.getAll()
// The Dao can SELECT words that are filtered.
val filteredWords: Flow<List<Word>> = wordDao.getFilteredWords()
@WorkerThread
suspend fun update(words: List<Word>) {
wordDao.update(words)
}
}
FilterViewModel:
class FilterViewModel(private val repository: WordRepository) : ViewModel() {
/**
* This viewModel can change the words stored in Room. This means
* if "filtered/current" is a parameter of word, then it can be
* set to true or false and then updated here with this method
*/
fun update(words: List<Word>) = viewModelScope.launch {
repository.update(words)
}
// To read the filtered words to find what to set as no longer
// filtered when the params change you can look here
val filteredWords: LiveData<List<Word>> = repository.filteredWords.asLiveData()
// To have the list of all words in the DB you can use this live
// From here you can you your other in puts to filter this
// when the user input changes.
val allWords: LiveData<List<Word>> = repository.allWords.asLiveData()
}
GridViewModel:
class GridViewModel(private val repository: WordRepository) : ViewModel() {
// The filtered words come from the repo that get it from the dao.
// This is read only, since we are just displaying them in the grid.
val filteredWords: LiveData<List<Word>> = repository.filteredWords.asLiveData()
}