Avoid passing individual view models around
For now, all view models are injected by Dagger at the start of the application in the top composable and then passed to the individual composable functions that actually need it. This has several disadvantages:
- the composables further up the tree have a lot of view models as parameters that are just passed down to other composables that actually need it. Also, adding a new view model adds a new function parameter to all composables up to the root resulting in a lot of unnecessary changes.
- all view models are initialized when the application starts and also start to listen for events, even if the respective screen is not shown (yet)
- there can always only be a single instance of a certain view model (unless we initialize two or more of them in the top composable). That would forbid us to support displaying several chats at the same time in separate windows (the Pidgin (?) way of doing things). That one might not be a big deal, though.
I would propose the following:
- implement a
ViewModelProvider
similar to the one available in Android that could either hold all view models from the application start on (not resolving the third point) or instantiating them on demand - have a base
ViewModel
class/interface that provides the methodsonInit()
andonCleared()
where such things as adding/removing the view model from theeventBus
can be done - adopt the convention of only instantiating view models within composables called
Screen
(e.g.MainScreen
for the view model handling the global state,PrivateMessageScreen
for the contact list view model) and always provide a second composable with the same name that takes the view model as a parameter (allowing for easier testability). The view model should always be instantiated as down in the UI tree as possible and as up in the tree as necessary.Screen
s could be seen as analog toFragment
orActivity
in the Android world. - implement a helper composable function called
Screen
that takes a view model class name and does the repetitive process of obtaining the view model from the provider, callonInit()
andonCleared()
when appropiate (the composeDisposableEffect
will come to handy here). Additionally,Screen
would take a composable function where the view model is available (similar to theApplicationScope
that exposesexitApplication
and is available afterrunApplication
).
As a rough sketch, the usage might then look like the following:
@Composable
fun PrivateMessageScreen() = Screen(ContactListViewModel::class) {
PrivateMessageScreen(viewModel)
}
@Composable
fun PrivateMessageScreen(viewModel: ContactListViewModel) {
// actual content using the viewModel
}