Android Dagger 2 Injection For Fragment (Kotlin)

May 14, 2018

Refer to Setup Dagger 2 For Android Kotlin for general Dagger 2 setup with Activity, while this article shall focus on dependency injection in Fragment only.

NOTE: This article depends on classes declared in Setup Dagger 2 For Android Kotlin.

Application Class

Add HasSupportFragmentInjector and fragmentInjector to Application class.

class LuaApp : Application(), HasActivityInjector, HasSupportFragmentInjector {
    @Inject
    lateinit var activityInjector: DispatchingAndroidInjector<Activity>

    @Inject
    lateinit var fragmentInjector: DispatchingAndroidInjector<Fragment>

    override fun onCreate() {
        super.onCreate()
        // initialize Dagger
        DaggerAppComponent.builder().application(this).build().inject(this)
    }


    override fun activityInjector(): AndroidInjector<Activity> = activityInjector

    override fun supportFragmentInjector(): AndroidInjector<Fragment> = fragmentInjector
}

NOTE: If the above is not configured properly, you will bump into exception Caused by: java.lang.IllegalArgumentException: No injector was found for ... when AndroidSupportInjection.inject is called in Fragment.

FragmentModule

Create FragmentModule class to declare Fragment for injection.

@Module
abstract class FragmentModule {
    @ContributesAndroidInjector
    internal abstract fun contributePhotoPinListFragment(): PhotoPinListFragment
}

AppComponent

Edit AppComponent to include @Component(modules = [..., (FragmentModule::class)])

@Singleton
@Component(modules = [(AndroidInjectionModule::class), (AppModule::class), (ActivityModule::class), (FragmentModule::class)])
interface AppComponent {
    @Component.Builder
    interface Builder {
        @BindsInstance
        fun application(application: Application): Builder

        fun build(): AppComponent
    }

    fun inject(luaApp: LuaApp)
}

Fragment

Include AndroidSupportInjection.inject at Fragment.onAttach.

Access ViewModel at Fragment.onActivityCreated.

class PhotoPinListFragment : Fragment() {

    @Inject
    lateinit var viewModelFactory: ViewModelFactory
    private lateinit var viewModel: PhotoPinListViewModel

    override fun onActivityCreated(savedInstanceState: Bundle?) {
        viewModel = ViewModelProviders.of(this, viewModelFactory).get(PhotoPinListViewModel::class.java)
    }

    override fun onAttach(context: Context) {
        AndroidSupportInjection.inject(this)
        super.onAttach(context)
    }
}

ViewModelModule

Remember to declare PhotoPinListViewMode at ViewModelModule.

@Module
internal abstract class ViewModelModule {
    @Binds
    @IntoMap
    @ViewModelKey(MainViewModel::class)
    abstract fun bindMainViewModel(viewModel: MainViewModel): ViewModel

    @Binds
    @IntoMap
    @ViewModelKey(PhotoPinListViewModel::class)
    abstract fun bindPhotoPinListViewModel(viewModel: PhotoPinListViewModel): ViewModel
}

References:

This work is licensed under a
Creative Commons Attribution-NonCommercial 4.0 International License.