I inject my dependencies in Activities and Fragments on the preferred way with AndroidInjection.inject(this)
. I have the recommended ViewmodelFactory to inject the viewmodel. My injections are working in Activities and Fragments. However I face a problem with BottomSheetDialogFragment, because I am not allowed to specify BottomSheetDialogFragment as this
. So my @Inject lateinit var viewModelFactory: ViewModelFactory
is not initialized. I believe that injection should be possible because the BottomSheetDialogFragment should be subclass of the Fragment class, but it looks like it is not. I use android.x which I believe can cause also problem. Is that possible that it is not supported by Dagger yet?
What way should I implement my ViewModelFactory injection?
Update: When I try to inject a fragment with AndroidInjection.inject(this)
, it is not possible either with the androidx.fragment.app.Fragment
only with android.app.Fragement
. I extend my Fragments with DaggerFragment
and they work as expected.
First of all set up Dagger in MyApp class. <layout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:bind="http://schemas.android.com/tools"> <data> <variable name="viewModel" type="com.
Setup a Dagger 2 Module Then setup the Dagger 2 Module with that provide the ViewModel. Like normal ViewModel creation, use ViewModelProvider and the Factory to create it. We'll need to pass in the dependencies which can be automatically injected by Dagger 2 using the conventional approach.
Here is how I have provided ViewModel into BottomSheetDialogFragment(). First of all set up Dagger in MyApp class.
class MyApp : Application(), HasActivityInjector, HasSupportFragmentInjector {
@Inject
lateinit var activityInjector: DispatchingAndroidInjector<Activity>
@Inject
lateinit var supportFragmentInjector: DispatchingAndroidInjector<Fragment>
override fun onCreate() {
super.onCreate()
initDagger()
}
override fun activityInjector(): AndroidInjector<Activity> = activityInjector
override fun supportFragmentInjector(): AndroidInjector<Fragment> = supportFragmentInjector
private fun initDagger(){
DaggerAppComponent
.builder()
.application(this)
.build()
.injectApp(this)
}
Then AppComponent class
@Singleton
@Component(
modules = [
AppModule::class,
UiModule::class,
AndroidSupportInjectionModule::class,
AndroidInjectionModule::class
])interface AppComponent : AndroidInjector<MyApp> {
@Component.Builder
interface Builder {
@BindsInstance
fun application(application: MyApp): Builder
fun build(): AppComponent
}
fun injectApp(app: MyApp)
}
Here we interested in UiModule::class
@Module
abstract class UiModule {
@Binds
abstract fun bindViewModelFactory(factory: ViewModelFactory): ViewModelProvider.Factory
@PerFragment
@ContributesAndroidInjector(modules = [(FilterModule::class)])
abstract fun contributeFilterFragment(): FilterFragment
}
you already have ViewModelFactory , so I don't paste this code. Then FilterModule
@Module
abstract class FilterModule {
@Binds
@IntoMap
@PerFragment
@ViewModelKey(FilterViewModel::class)
abstract fun bindViewModel(viewModel: FilterViewModel): ViewModel
}
and finally FilterFragment and FilterViewModel
class FilterFragment : BottomSheetDialogFragment() {
@Inject
lateinit var factory: ViewModelProvider.Factory
private lateinit var binding: FragmentFilterBinding
private lateinit var viewModel: FilterViewModel
override fun onAttach(context: Context?) {
AndroidSupportInjection.inject(this)
super.onAttach(context)
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
binding = getDataBinding(inflater, R.layout.fragment_filter, container)
viewModel = getViewModel(factory)
binding.viewModel = viewModel
return binding.root
}
}
class FilterViewModel @Inject constructor(private val testUseCase:TestUseCase) : BaseViewModel() {
//do something
}
fragment_filter layout
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:bind="http://schemas.android.com/tools">
<data>
<variable
name="viewModel"
type="com.myapp.presentation.screen.filter.FilterViewModel" />
</data>
.......
getDataBinding() and getViewModel() are extension functions
fun <T : ViewDataBinding> Fragment.getDataBinding(inflater: LayoutInflater, @LayoutRes layoutId: Int, container: ViewGroup?): T =
DataBindingUtil.inflate(inflater, layoutId, container, false)
inline fun <reified T : BaseViewModel> Fragment.getViewModel(factory: ViewModelProvider.Factory = ViewModelProviders.DefaultFactory(activity!!.application)): T =
ViewModelProviders.of(this, factory).get(T::class.java)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With