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
ViewModelProvidersimilar 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
ViewModelclass/interface that provides the methodsonInit()andonCleared()where such things as adding/removing the view model from theeventBuscan be done - adopt the convention of only instantiating view models within composables called
Screen(e.g.MainScreenfor the view model handling the global state,PrivateMessageScreenfor 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.Screens could be seen as analog toFragmentorActivityin the Android world. - implement a helper composable function called
Screenthat takes a view model class name and does the repetitive process of obtaining the view model from the provider, callonInit()andonCleared()when appropiate (the composeDisposableEffectwill come to handy here). Additionally,Screenwould take a composable function where the view model is available (similar to theApplicationScopethat exposesexitApplicationand 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
}