Setting Up Dependency Injection Using Anvil: A Step-by-Step Guide | by Xi Wei | Mar, 2025

Anvil is a clean and powerful library that simplifies dependency injection by building on top of Dagger. It removes a lot of boilerplate and is easy to use. There are some great articles explaining how Anvil works and why you might want to use it. However, the exact setup documentation is limited, so I’m creating this step-by-step guide to help newcomers get started.

While Anvil truly shines in multi-module projects, I’ll keep everything in a single module for simplicity. In this guide, I’ll demonstrate dependency injection with a simple GreetingViewModel and GreetingRepository.

libs.versions.toml

[versions]
...
kotlin = "2.0.21"
dagger = "2.55"
anvil = "2.5.1"

[libraries]
...
dagger = { group = "com.google.dagger", name = "dagger", version.ref = "dagger" }
dagger-compiler = { group = "com.google.dagger", name = "dagger-compiler", version.ref = "dagger" }

[plugins]
...
kotlin-kapt = { id = "org.jetbrains.kotlin.kapt", version.ref = "kotlin" }
anvil = { id = "com.squareup.anvil", version.ref = "anvil" }

build.gradle.ktx (root)

plugins {
...
alias(libs.plugins.kotlin.kapt) apply false
alias(libs.plugins.anvil) apply false
}

build.gradle.ktx (:app)

plugins {
...
alias(libs.plugins.kotlin.kapt)
alias(libs.plugins.anvil)
}

dependencies {
...
implementation(libs.dagger)
kapt(libs.dagger.compiler)
}

AppScope.kt

abstract class AppScope private constructor()

AppComponent.kt

import com.squareup.anvil.annotations.MergeComponent

@MergeComponent(AppScope::class)
interface AppComponent {
fun inject(activity: MainActivity)
}

MainApplication.kt

class MainApplication : Application() {
val appComponent: AppComponent by lazy { DaggerAppComponent.create() }
}

AndroidManifest.xml

   android:name=".MainApplication"
... >

GreetingRepository.kt

interface GreetingRepository {
fun getName(): String
}

GreetingRepositoryImpl.kt

@ContributesBinding(AppScope::class)
class GreetingRepositoryImpl @Inject constructor() : GreetingRepository {
override fun getName() = "Anvil"
}

ViewModelKey.kt

import androidx.lifecycle.ViewModel
import dagger.MapKey
import kotlin.reflect.KClass

@MapKey
@Retention(AnnotationRetention.RUNTIME)
annotation class ViewModelKey(val value: KClass)

GreetingViewModel.kt

import androidx.lifecycle.ViewModel
import com.squareup.anvil.annotations.ContributesMultibinding
import javax.inject.Inject

@ContributesMultibinding(AppScope::class, boundType = ViewModel::class)
@ViewModelKey(GreetingViewModel::class)
class GreetingViewModel @Inject constructor(
private val greetingRepository: GreetingRepository
) : ViewModel() {
fun getName() = greetingRepository.getName()
}

ViewModelFactory.kt

import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import com.squareup.anvil.annotations.ContributesBinding
import javax.inject.Inject
import javax.inject.Provider

@ContributesBinding(AppScope::class)
class ViewModelFactory @Inject constructor(
private val creators: @JvmSuppressWildcards Map, Provider>
) : ViewModelProvider.Factory {
override fun create(modelClass: Class): T {
val creator = creators[modelClass] ?: creators.entries.firstOrNull {
modelClass.isAssignableFrom(it.key)
}?.value ?: throw IllegalArgumentException("Unknown ViewModel class: $modelClass")

@Suppress("UNCHECKED_CAST")
return creator.get() as T
}
}

MainActivity.kt

class MainActivity : ComponentActivity() {
@Inject lateinit var viewModelFactory: ViewModelProvider.Factory
private val viewModel: GreetingViewModel by viewModels { viewModelFactory }

override fun onCreate(savedInstanceState: Bundle?) {
(application as MainApplication).appComponent.inject(this)
super.onCreate(savedInstanceState)
setContent {
AnvilSetupTheme {
Greeting(viewModel)
}
}
}
}

@Composable
fun Greeting(viewModel: GreetingViewModel) {
Text(text = "Hello ${viewModel.getName()}!")
}

In this guide, we walked through setting up Anvil with Dagger in a single-module Android project. We defined a custom scope, created a component, and injected a ViewModel using Anvil’s bindings and multibindings. Anvil greatly reduces the boilerplate usually associated with Dagger, making DI setup easier and more maintainable.

If you’re working with a multi-module project, Anvil’s benefits become even more powerful with automatic code generation and module separation. But even in a simple setup like this, it can save you a lot of time.

👉 Please check out my GitHub repo for the full implementation:

Let me know if you’d like a follow-up post on multi-module configuration!

Leave a Comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.