Search code examples
androidkotlingradleandroid-jetpack-composedagger-hilt

Dagger Hilt - "Cannot create an instance of ViewModel" when dependency injection is used


I am currently facing an issue with incorporating dependency injection into my app, which I developed using Jetpack Compose.

I have included the following dependencies and plugins for Dagger Hilt:

Project level gradle file:

plugins {
    ...
    id("com.google.dagger.hilt.android") version "2.51.1" apply false
}

Module level gradle file:

plugins {
    ...

    id("kotlin-kapt")
    id("com.google.dagger.hilt.android")
}

dependencies {
    ...

    //hilt
    implementation(libs.hilt.android)
    kapt(libs.hilt.android.compiler)
    implementation(libs.androidx.hilt.navigation.compose)
    kapt(libs.androidx.hilt.compiler)
    implementation(libs.androidx.hilt.navigation.fragment)
}

kapt {
    correctErrorTypes = true
}

With this version catalog:

[versions]
agp = "8.5.0"
hiltAndroid = "2.51.1"
hiltAndroidCompiler = "2.48"
hiltLifecycleViewmodel = "1.0.0-alpha03"
hiltNavigationCompose = "1.2.0"
kotlin = "1.9.0"
coreKtx = "1.13.1"

[libraries]
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
androidx-hilt-compiler = { module = "androidx.hilt:hilt-compiler", version.ref = "hiltNavigationCompose" }
androidx-hilt-navigation-compose = { module = "androidx.hilt:hilt-navigation-compose", version.ref = "hiltNavigationCompose" }
androidx-hilt-navigation-fragment = { module = "androidx.hilt:hilt-navigation-fragment", version.ref = "hiltNavigationCompose" }
hilt-android = { module = "com.google.dagger:hilt-android", version.ref = "hiltAndroid" }
hilt-android-compiler = { module = "com.google.dagger:hilt-android-compiler", version.ref = "hiltAndroidCompiler" }
#hilt-android-gradle-plugin seems to be greyed out - it isn't being used
hilt-android-gradle-plugin = { module = "com.google.dagger:hilt-android-gradle-plugin", version.ref = "hiltAndroid" }

[plugins]
#No Hilt-Related plugins, only android-application and kotlin

I had created a HiltViewModel, as such:

@HiltViewModel
class GameViewModel @Inject constructor(
    private val sample: Sample
) : ViewModel() {

   ...
}

where Sample is a dummy class which is defined as such:

class Sample (
    context: Context
)

The Module for injecting my dependencies and my application class are also included, for reference:

import android.app.Application
import com.example.cheesechase.gyroscope.Sample
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton

@Module
@InstallIn(SingletonComponent::class)
object AppModule {
    @Provides
    @Singleton
    fun giveSample(context: Application): Sample {
        return Sample(context)
    }
}

I've given the @AndroidEntryPoint annotation to my MainActivity class, from where I call the navigation composable (I use Compose Navigation). I Initialise my viewmodel inside the Navigation Composable using

val viewModel = hiltViewModel<GameViewModel>()

and pass it to my screens as the viewModel variable. Now, when I try to run the code, I get the following exception:

java.lang.RuntimeException: Cannot create an instance of class com.example.cheesechase.GameViewModel
    at androidx.lifecycle.viewmodel.internal.JvmViewModelProviders.createViewModel(JvmViewModelProviders.kt:40)
    at androidx.lifecycle.ViewModelProvider$NewInstanceFactory.create(ViewModelProvider.android.kt:193)
    at androidx.lifecycle.ViewModelProvider$AndroidViewModelFactory.create(ViewModelProvider.android.kt:317)
    at androidx.lifecycle.ViewModelProvider$AndroidViewModelFactory.create(ViewModelProvider.android.kt:299)
    at androidx.lifecycle.ViewModelProvider$AndroidViewModelFactory.create(ViewModelProvider.android.kt:273)
    at androidx.lifecycle.SavedStateViewModelFactory.create(SavedStateViewModelFactory.kt:128)
    at dagger.hilt.android.internal.lifecycle.HiltViewModelFactory.create(HiltViewModelFactory.java:172)
    at dagger.hilt.android.internal.lifecycle.HiltViewModelFactory.create(HiltViewModelFactory.java:172)
    at androidx.lifecycle.ViewModelProvider$Factory.create(ViewModelProvider.android.kt:158)
    at androidx.lifecycle.viewmodel.ViewModelProviderImpl.getViewModel$lifecycle_viewmodel_release(ViewModelProviderImpl.kt:67)
    at androidx.lifecycle.viewmodel.ViewModelProviderImpl.getViewModel$lifecycle_viewmodel_release$default(ViewModelProviderImpl.kt:47)
    at androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.android.kt:91)
    at androidx.lifecycle.viewmodel.compose.ViewModelKt__ViewModelKt.get(ViewModel.kt:162)
    at androidx.lifecycle.viewmodel.compose.ViewModelKt.get(Unknown Source:1)
    at androidx.lifecycle.viewmodel.compose.ViewModelKt__ViewModel_androidKt.viewModel(ViewModel.android.kt:124)
    at androidx.lifecycle.viewmodel.compose.ViewModelKt.viewModel(Unknown Source:1)
    at com.example.cheesechase.navigation.NavigationKt.Navigation(Navigation.kt:84)
    at com.example.cheesechase.MainActivity$onCreate$1$1$1.invoke(MainActivity.kt:38)
    at com.example.cheesechase.MainActivity$onCreate$1$1$1.invoke(MainActivity.kt:36)
    at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:118)
    at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:35)
    at androidx.compose.material3.ScaffoldKt$ScaffoldLayout$1$1$1$bodyContentPlaceables$1.invoke(Scaffold.kt:239)
    at androidx.compose.material3.ScaffoldKt$ScaffoldLayout$1$1$1$bodyContentPlaceables$1.invoke(Scaffold.kt:221)
    at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:109)
    at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:35)
    at androidx.compose.ui.layout.LayoutNodeSubcompositionsState$subcompose$3$1$1.invoke(SubcomposeLayout.kt:989)
    at androidx.compose.ui.layout.LayoutNodeSubcompositionsState$subcompose$3$1$1.invoke(SubcomposeLayout.kt:476)
    at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:109)
    at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:35)
    at androidx.compose.runtime.ActualJvm_jvmKt.invokeComposable(ActualJvm.jvm.kt:90)
    at androidx.compose.runtime.ComposerImpl.doCompose(Composer.kt:3302)
    at androidx.compose.runtime.ComposerImpl.composeContent$runtime_release(Composer.kt:3235)
    at androidx.compose.runtime.CompositionImpl.composeContent(Composition.kt:723)
    at androidx.compose.runtime.Recomposer.composeInitial$runtime_release(Recomposer.kt:1071)
    at androidx.compose.runtime.ComposerImpl$CompositionContextImpl.composeInitial$runtime_release(Composer.kt:3599)
    at androidx.compose.runtime.CompositionImpl.composeInitial(Composition.kt:631)
    at androidx.compose.runtime.CompositionImpl.setContent(Composition.kt:617)
    at androidx.compose.ui.layout.LayoutNodeSubcompositionsState.subcomposeInto(SubcomposeLayout.kt:499)
    at androidx.compose.ui.layout.LayoutNodeSubcompositionsState.subcompose(SubcomposeLayout.kt:471)

I have tried moving the initialization to the MainActivity, other places too, to no avail. I tried changing and switching up the gradle plugins and dependencies. Also, the code works fine (runs without errors) when I include no dependencies in my GameViewModel constructor, like this:

@HiltViewModel
class GameViewModel @Inject constructor() : ViewModel() {...}

Solution

  • The problem is that you mix different Hilt versions. Where you generally use version "2.51.1", hilt-android-compiler (com.google.dagger:hilt-android-compiler) uses only "2.48".

    I do not know why you even have two different Hilt versions in your version catalog, but you should remove this one:

    hiltAndroidCompiler = "2.48"
    

    Now just replace its usage with the correct version hiltAndroid and everything should work.


    While we're at it:

    1. You should define the hilt plugin in your version catalog as well:

      [plugins]
      hilt-android = { id = "com.google.dagger.hilt.android", version.ref = "hiltAndroid" }
      

      Now you can replace id("com.google.dagger.hilt.android") in your gradle files with alias(libs.plugins.hilt.android) (make sure to also use apply false in your project level gradle file).
      This way you only have one single location where you define the Hilt version, and that is in your version catalog. If you want to update the version, just update hiltAndroid.

    2. You do not need the following dependencies, remove them:

      kapt(libs.androidx.hilt.compiler)
      implementation(libs.androidx.hilt.navigation.fragment)
      

      You can then remove the grayed-out entries from the version catalog so it gets less confusing.

    3. You do not need a Dagger Module for what you are doing if you annotate your Sample class like this:

      @Singleton
      class Sample @Inject constructor(
          @ApplicationContext context: Context,
      )
      

      The application context can be injected by Hilt out of the box.

    4. Do not pass view model instances around. Once obtained with hiltViewModel you should only pass its properties and functions to other composables. If your properties are Flows or States, then only pass their values.