Does anyone here have any experience or good sugge...
# compose
h
Does anyone here have any experience or good suggestions with using Compose navigation with Dagger2? So far I have followed this article, but what I am struggeling with is providing dependencies from our main module to the new module.
p
Perhaps passing the DaggerComponent down to the navigation sub graphs. In fact you can pass child DaggerComponents to each subgraph. Similar to Uber ribs, not sure if you are familiar with that project.
m
Honestly, I tend to prefer creating an interface that will provide me a ViewModelFactory. I do this because if you want to use a SavedStateHandle, you have to have the store owner available at creation time. So adding a level of indirection helps.
Copy code
interface ViewModelFactoryCreator {
    fun create(owner: ViewModelStoreOwner): ViewModelProvider.Factory
}

class MyViewModel: ViewModel()

@Composable
fun MyComposable(viewModelFactoryCreator: ViewModelFactoryCreator) {
    val viewModel: MyViewModel = viewModel(
        factory = viewModelFactoryCreator.create(LocalViewModelStoreOwner.current!!)
    )
}
I provide an implementation of the above interface in my dagger graph, and inject it to the activity, and just pass it along either as a parameter, or through a composition local (I prefer the former). If you want a view model scoped to your compose destination, the "NavBackStackEntry" implements ViewModelStoreOwner.
I prefer to make an interface that will return me the ViewModelFactory:
Copy code
interface ViewModelFactoryCreator {
    fun create(owner: SavedStateRegistryOwner): ViewModelProvider.Factory
}

class DefaultViewModelFactoryCreator: ViewModelFactoryCreator {
    override fun create(owner: SavedStateRegistryOwner): ViewModelProvider.Factory {
        return object: AbstractSavedStateViewModelFactory(owner, null) {
            override fun <T : ViewModel?> create(
                key: String,
                modelClass: Class<T>,
                handle: SavedStateHandle
            ): T {
                return when (modelClass) {
                    MyViewModel::class.java -> MyViewModel(handle)
                    else -> throw IllegalArgumentException("unknown view model $modelClass")
                } as T
            }
        }
    }
}

class MyViewModel(val savedStateHandle: SavedStateHandle): ViewModel()

@Composable
fun MyComposable(viewModelFactoryCreator: ViewModelFactoryCreator) {
    val viewModel: MyViewModel = viewModel(
        factory = viewModelFactoryCreator.create(LocalSavedStateRegistryOwner.current)
    )
}
You can then create the implementation in your dagger graph with a when clause or use @IntoSet or some other mechanism if you want to be a bit more modular. This lets you hide all the dependencies that are needed to create the individual view models within your dagger graph, so that they are not leaked to the UI layer.