Search code examples
androidandroid-jetpack-composeandroid-viewmodeldagger-hilt

Hilt viewModel and Compose not working together


It seems like the official Android docs way of using Compose, Hilt and ViewModel injection with Navigation does not work.

I have the following setup:

A main screen, which only hosts the nav controller. The Home screen shows a button to. navigation to the name screen which has a view model.

@Composable
fun MainScreen() {
    val navController = rememberNavController()
    NavHost(navController, startDestination = "home") {
        composable("home") {
            HomeScreen() {
                navController.navigate("my/Name")
            }
        }

        composable("my/{name}") { navBackStackEntry ->
            val viewModel: NameScreenViewModel = hiltViewModel(navBackStackEntry)
            NameScreen(viewModel)
        }
    }
}
@Composable
fun HomeScreen(onButtonClicked: () -> Unit) {
    Button(onClick = onButtonClicked, modifier = Modifier.padding(8.dp)) {
        Text("Continue")
    }
}
@HiltViewModel
class NameScreenViewModel @Inject constructor(private val test: TestClass): ViewModel() {
    var name = MutableLiveData("empty")

    fun change() {
        name.value = test.load()
    }
}

@Composable
fun NameScreen(viewModel: NameScreenViewModel) {
    val name by viewModel.name.observeAsState()

    Column {
        Text("Hi there, $name")

        Button(onClick = { viewModel.change() }) {
            Text("Click me!")
        }
    }
}

Activity and Application have the correct annotations:

@AndroidEntryPoint
class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            ComposeViewModelTheme {
                // A surface container using the 'background' color from the theme
                Surface(color = MaterialTheme.colors.background) {
                    MainScreen()
                }
            }
        }
    }
}
@HiltAndroidApp
class MainApplication : Application() {
}

The injected class is very simple and is even registered for being provided instead of using the constructor annotation:

class TestClass {
    fun load(): String {
        return "name"
    }
}
@Module
@InstallIn(SingletonComponent::class)
class AppModule {
    @Provides
    @Singleton
    fun providesTestClass(): TestClass {
        return TestClass()
    }
}

If using hiltViewModel as above, the app will crash with the error

java.lang.IllegalStateException: Given component holder class eu.meecolabs.composeviewmodel.MainActivity does not implement interface dagger.hilt.internal.GeneratedComponent or interface dagger.hilt.internal.GeneratedComponentManager
        at dagger.hilt.EntryPoints.get(EntryPoints.java:62)
        at dagger.hilt.android.internal.lifecycle.HiltViewModelFactory.createInternal(HiltViewModelFactory.java:129)
        at androidx.hilt.navigation.HiltViewModelFactory.create(HiltNavBackStackEntry.kt:56)
        at eu.meecolabs.composeviewmodel.ui.ComposableSingletons$MainScreenKt$lambda-1$1.invoke(MainScreen.kt:26)
        at eu.meecolabs.composeviewmodel.ui.ComposableSingletons$MainScreenKt$lambda-1$1.invoke(MainScreen.kt:25)
        at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:116)
        at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:34)
        at androidx.navigation.compose.NavHostKt$NavHost$6$1.invoke(NavHost.kt:146)
        at androidx.navigation.compose.NavHostKt$NavHost$6$1.invoke(NavHost.kt:145)
        at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:107)
        at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:34)
        at androidx.compose.runtime.CompositionLocalKt.CompositionLocalProvider(CompositionLocal.kt:215)
        at androidx.compose.runtime.saveable.SaveableStateHolderImpl.SaveableStateProvider(SaveableStateHolder.kt:84)
        at androidx.navigation.compose.NavBackStackEntryProviderKt.SaveableStateProvider(NavBackStackEntryProvider.kt:59)
        at androidx.navigation.compose.NavBackStackEntryProviderKt.access$SaveableStateProvider(NavBackStackEntryProvider.kt:1)
        at androidx.navigation.compose.NavBackStackEntryProviderKt$LocalOwnersProvider$1.invoke(NavBackStackEntryProvider.kt:51)
        at androidx.navigation.compose.NavBackStackEntryProviderKt$LocalOwnersProvider$1.invoke(NavBackStackEntryProvider.kt:50)
        at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:107)
        at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:34)
        at androidx.compose.runtime.CompositionLocalKt.CompositionLocalProvider(CompositionLocal.kt:215)
        at androidx.navigation.compose.NavBackStackEntryProviderKt.LocalOwnersProvider(NavBackStackEntryProvider.kt:46)
        at androidx.navigation.compose.NavHostKt$NavHost$6.invoke(NavHost.kt:145)
        at androidx.navigation.compose.NavHostKt$NavHost$6.invoke(NavHost.kt:144)
        at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:116)
        at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:34)
        at androidx.compose.animation.CrossfadeKt$Crossfade$1$1.invoke(Crossfade.kt:74)
        at androidx.compose.animation.CrossfadeKt$Crossfade$1$1.invoke(Crossfade.kt:69)
        at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:107)
        at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:34)
        at androidx.compose.animation.CrossfadeKt.Crossfade(Crossfade.kt:86)
        at androidx.navigation.compose.NavHostKt.NavHost(NavHost.kt:144)
        at androidx.navigation.compose.NavHostKt$NavHost$7.invoke(Unknown Source:13)
        at androidx.navigation.compose.NavHostKt$NavHost$7.invoke(Unknown Source:10)
        at androidx.compose.runtime.RecomposeScopeImpl.compose(RecomposeScopeImpl.kt:140)
        at androidx.compose.runtime.ComposerImpl.recomposeToGroupEnd(Composer.kt:2156)
        at androidx.compose.runtime.ComposerImpl.skipCurrentGroup(Composer.kt:2399)
        at androidx.compose.runtime.ComposerImpl$doCompose$2$5.invoke(Composer.kt:2580)
        at androidx.compose.runtime.ComposerImpl$doCompose$2$5.invoke(Composer.kt:2573)
        at androidx.compose.runtime.SnapshotStateKt.observeDerivedStateRecalculations(SnapshotState.kt:540)
        at androidx.compose.runtime.ComposerImpl.doCompose(Composer.kt:2566)
2021-07-27 17:46:40.678 22501-22501/? E/AndroidRuntime:     at androidx.compose.runtime.ComposerImpl.recompose$runtime_release(Composer.kt:2542)
        at androidx.compose.runtime.CompositionImpl.recompose(Composition.kt:613)
        at androidx.compose.runtime.Recomposer.performRecompose(Recomposer.kt:764)
        at androidx.compose.runtime.Recomposer.access$performRecompose(Recomposer.kt:103)
        at androidx.compose.runtime.Recomposer$runRecomposeAndApplyChanges$2$2.invoke(Recomposer.kt:447)
        at androidx.compose.runtime.Recomposer$runRecomposeAndApplyChanges$2$2.invoke(Recomposer.kt:416)
        at androidx.compose.ui.platform.AndroidUiFrameClock$withFrameNanos$2$callback$1.doFrame(AndroidUiFrameClock.android.kt:34)
        at androidx.compose.ui.platform.AndroidUiDispatcher.performFrameDispatch(AndroidUiDispatcher.android.kt:109)
        at androidx.compose.ui.platform.AndroidUiDispatcher.access$performFrameDispatch(AndroidUiDispatcher.android.kt:41)
        at androidx.compose.ui.platform.AndroidUiDispatcher$dispatchCallback$1.doFrame(AndroidUiDispatcher.android.kt:69)
        at android.view.Choreographer$CallbackRecord.run(Choreographer.java:965)
        at android.view.Choreographer.doCallbacks(Choreographer.java:791)
        at android.view.Choreographer.doFrame(Choreographer.java:722)
        at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:952)
        at android.os.Handler.handleCallback(Handler.java:883)
        at android.os.Handler.dispatchMessage(Handler.java:100)
        at android.os.Looper.loop(Looper.java:214)
        at android.app.ActivityThread.main(ActivityThread.java:7356)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:491)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:940)

I am using the latest Android Studio Preview and the following dependency versions:

ext {
        compose_version = '1.0.0-rc02'
        hilt_version = '2.38'
    }
    dependencies {
        classpath "com.android.tools.build:gradle:7.1.0-alpha04"
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.10"

        classpath "com.google.dagger:hilt-android-gradle-plugin:$hilt_version"

        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
implementation 'androidx.core:core-ktx:1.6.0'

    implementation 'androidx.appcompat:appcompat:1.3.1'

    implementation 'com.google.android.material:material:1.4.0'

    implementation "androidx.compose.ui:ui:$compose_version"
    implementation "androidx.compose.material:material:$compose_version"
    implementation "androidx.compose.ui:ui-tooling-preview:$compose_version"

    implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.3.1'

    implementation "androidx.compose.runtime:runtime-livedata:$compose_version"
    implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.3.1'

    implementation 'androidx.hilt:hilt-lifecycle-viewmodel:1.0.0-alpha03'
    implementation 'androidx.lifecycle:lifecycle-viewmodel-compose:1.0.0-alpha07'

    implementation 'androidx.activity:activity-compose:1.3.0-rc02'

    implementation "androidx.navigation:navigation-compose:2.4.0-alpha05"
    implementation 'androidx.hilt:hilt-navigation-compose:1.0.0-alpha03'

    implementation "com.google.dagger:hilt-android:$hilt_version"
    kapt "com.google.dagger:hilt-compiler:$hilt_version"
    kapt 'androidx.hilt:hilt-compiler:1.0.0'

I really do not know what else to try. I have checked so many different combinations already and read through the hand full or blog posts or SO issues, but all seem to be using outdated libraries or there does not seem to be a real solution.


Solution

  • The solution seems to be updating dependencies. Since I posted this question, Hilt released v2.38.1 and Compose was released in v1.0.0 (final).

    That combination will work as expected. 👍