Using the old ways with Dagger 2 for injecting ViewModel, how to create multiple instance of the same ViewModel for ViewPager?
@Singleton
class ViewModelProviderFactory @Inject constructor(
private val creators: Map<Class<out ViewModel>, @JvmSuppressWildcards Provider<ViewModel>>
) : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
val creator = creators[modelClass] ?: creators.entries.firstOrNull {
modelClass.isAssignableFrom(it.key)
}?.value
?: throw IllegalArgumentException("Unknown ViewModel $modelClass. You probably forgot to include your ViewModel module to ViewModelFactory module or map it with the ViewModelKey annotation?")
@Suppress("UNCHECKED_CAST")
return creator.get() as T
}
}
@Module(
includes = [
SampleViewModelModule::class,
SampleCopyViewModelModule::class,
]
)
abstract class ViewModelFactoryModule {
@Binds
abstract fun bindViewModelFactory(viewModelProviderFactory: ViewModelProviderFactory): ViewModelProvider.Factory
}
Modules
@Module
object SampleViewModelModule {
@Provides
@IntoMap
@ViewModelKey(SampleViewModel::class)
fun provideSampleViewModel(
useCase: GetSampleUseCase
): ViewModel = MessariPriceViewModel(useCase)
@Provides
@Named("FragmentOne")
fun provideSampleViewModelProvider(
owner: FragmentOne,
factory: ViewModelProvider.Factory
) = ViewModelProvider(owner, factory)[SampleViewModel::class.java]
}
@Module
object SampleCopyViewModelModule {
@Provides
@IntoMap
@ViewModelKey(SampleViewModel::class)
fun provideSampleCopyViewModel(
useCase: GetMessariPricesUseCase
): ViewModel = MessariPriceViewModel(useCase)
@Provides
@Named("FragmentTwo")
fun provideSampleCopyViewModelProvider(
owner: FragmentTwo,
factory: ViewModelProvider.Factory
) = ViewModelProvider(owner, factory)[SampleViewModel::class.java]
}
@Module
abstract class FragmentMainBuildersModule {
@ContributesAndroidInjector(modules = [SampleModelModule::class])
abstract fun contributeFragmentOne(): FragmentOne
@ContributesAndroidInjector(modules = [SampleCopyModelModule::class])
abstract fun contributeFragmentTwo(): FragmentTwo
}
@Module
abstract class ActivityBuildersModule {
@ContributesAndroidInjector(modules = [FragmentMainBuildersModule::class])
abstract fun contributeMainActivity(): MainActivity
}
Component
@Singleton
@Component(
modules = [
AndroidSupportInjectionModule::class, // Default module, always on top
ViewModelFactoryModule::class, // Injecting ViewModel happens here
ActivityBuildersModule::class, // Injecting Activity and Fragment happens here
]
)
interface AppComponent : AndroidInjector<App> {
// Override the builder
@Component.Builder
interface Builder {
// We can now access and inject Application class anywhere in this project
// as long as that consumer class is also injected/registered in Dagger
@BindsInstance
fun application(application: Application): Builder
fun build(): AppComponent
}
}
Accessing it in Fragment like the below will throw build error
@Inject
@Named("FragmentOne")
override lateinit var viewModel: SampleViewModel
Error
[Dagger/MapKeys] The same map key is bound more than once