Search code examples
androidandroid-jetpack-composedagger-hiltandroid-jetpack-navigation

Jetpack Compose Navigation with HiltViewModel: MainActivity does not implement interface dagger.hilt.internal.GeneratedComponent


I created a new composable activity project with the following dependencies:

implementation "androidx.navigation:navigation-compose:2.4.0-alpha09"
implementation "com.google.dagger:hilt-android:2.38.1"
implementation "androidx.hilt:hilt-navigation-compose:1.0.0-alpha03"

Then I created two composables, one with the Hilt's view model:

@Composable
fun Screen1(onClick: () -> Unit) {
    Column {
        Text(text = "Screen 1")
        Button(onClick = onClick) {
            Text("To Screen 2")
        }
    }
}

@HiltViewModel
class Screen2ViewModel @Inject constructor() : ViewModel()

@Composable
fun Screen2(viewModel: Screen2ViewModel = hiltViewModel()) {
    Text(text = "Screen 2")
}

It works if I try to render them. However, when I add the NavController:

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            val navController = rememberNavController()

            MyApplicationTheme {
                // A surface container using the 'background' color from the theme
                Surface(color = MaterialTheme.colors.background) {
                    NavHost(navController = navController, startDestination = "screen1") {
                        composable("screen1") {
                            Screen1() {
                                navController.navigate("screen2")
                            }
                        }
                        composable("screen2") {
                            Screen2()
                        }
                    }
                }
            }
        }
    }
}

My app crashes when I go to the screen 2 with the following error:

java.lang.IllegalStateException: Given component holder class com.sample.myapplication.MainActivity does not implement interface dagger.hilt.internal.GeneratedComponent or interface dagger.hilt.internal.GeneratedComponentManager

What am I doing wrong?

Full code:

package com.sample.myapplication

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.Column
import androidx.compose.material.Button
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Surface
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.ViewModel
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import com.sample.myapplication.ui.theme.MyApplicationTheme
import dagger.hilt.android.AndroidEntryPoint
import dagger.hilt.android.lifecycle.HiltViewModel
import javax.inject.Inject

@AndroidEntryPoint
class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            val navController = rememberNavController()

            MyApplicationTheme {
                // A surface container using the 'background' color from the theme
                Surface(color = MaterialTheme.colors.background) {
                    NavHost(navController = navController, startDestination = "screen1") {
                        composable("screen1") {
                            Screen1() {
                                navController.navigate("screen2")
                            }
                        }
                        composable("screen2") {
                            Screen2()
                        }
                    }
                }
            }
        }
    }
}

@Composable
fun Screen1(onClick: () -> Unit) {
    Column {
        Text(text = "Screen 1")
        Button(onClick = onClick) {
            Text("To Screen 2")
        }
    }
}

@HiltViewModel
class Screen2ViewModel @Inject constructor() : ViewModel()

@Composable
fun Screen2(viewModel: Screen2ViewModel = hiltViewModel()) {
    Text(text = "Screen 2")
}


Solution

  • According to the document of HiltViewModel

    Returns an existing HiltViewModel -annotated ViewModel or creates a new one scoped to the current navigation graph present on the {@link NavController} back stack. If no navigation graph is currently present then the current scope will be used, usually, a fragment or an activity.

    To fix the issue we have two solutions:

    1. Use viewModel() from import androidx.lifecycle.viewmodel.compose.viewModel
    2. Scope the instance of hiltViewModel() to the activity and pass to Screen2()
    3. Need to apply the Hilt Plugin as the next answer: Add some more to app build.gradle
    • apply plugin: kotlin-kapt
    • add hilt android compiler to dependency kapt "com.google.dagger:hilt-android-compiler:2.40" Create an extension of application and anotate with @HiltAndroidApp and use in your app.