Program crash after injecting ViewModel to the parameter of function inside MainActivity

54 views Asked by At

Yesterday, I've made a pretty simple template for mvvm+hilt+retrofit, to make some api call. Everything worked. Today, I've wanted to retrieve user location (latitude/longitude), but now, I'm facing problem with my ViewModel

After I will start the program, it'll crash immediately with following error:

Caused by: java.lang.IllegalStateException: Given component holder class androidx.compose.ui.tooling.PreviewActivity does not implement interface dagger.hilt.internal.GeneratedComponent or interface dagger.hilt.internal.GeneratedComponentManager

Turns out, it will crash whenever I will try to inject ViewModel inside of any function in MainAcitvity

How I can manage to fix it? I've tried many solutions from other posts, but with no luck so far... Here's some code:

Gradle module

plugins {
    id("com.android.application")
    id ("com.google.dagger.hilt.android")
    id("org.jetbrains.kotlin.android")
    id ("com.google.android.libraries.mapsplatform.secrets-gradle-plugin")
    id ("kotlin-kapt")
}
   ...
dependencies {    
 implementation("com.google.dagger:hilt-android:2.50") 
 kapt("com.google.dagger:hilt-compiler:2.50")
 implementation("androidx.hilt:hilt-navigation-compose:1.2.0")
}

Gradle, root

plugins {
    id("com.android.application") version "8.2.2" apply false
    id("org.jetbrains.kotlin.android") version "1.8.10" apply false
    id ("com.google.dagger.hilt.android") version "2.50" apply false
    id ("com.google.android.libraries.mapsplatform.secrets-gradle-plugin") version "2.0.1" apply false
}

ViewModel

@HiltViewModel
class MyViewModel @Inject constructor(
    private val repository: MainRepository,
    private val location: LocationClient
) : ViewModel() {
}

MainActivity

@AndroidEntryPoint
class MainActivity : ComponentActivity() {


    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        setContent {
            SomeTheme {
                test123()
            }
        }
    }
}

@Preview(showBackground = true)
@Composable
fun GreetingPreview() {
    SomeTheme {
        test123()
    }
}

@OptIn(ExperimentalPermissionsApi::class)
@Composable
fun test123(
    viewModel: MyViewModel = hiltViewModel()
) {

Application

@HiltAndroidApp
class MainApplication: Application() {}

also I have marked application in my manifest file

 android:name=".Application.MainApplication"
1

There are 1 answers

0
BenjyTec On

I think it is not possible to use hiltViewModel() in a @Preview Composable.

You can either forgo using a Preview and remove it from your code, or you can use a workaround suggested here or here.

You could seperate the Composable content and the Composable initializing the ViewModel like this:

@OptIn(ExperimentalPermissionsApi::class)
@Composable
fun test123Wrapper(
    // instead of using constructor injection, declare ViewModel in Composable body
) {
    val viewModel = hiltViewModel<MyViewModel>()
    val myValue by viewModel.myValue.collectAsState()
    val myFunction = viewModel.myFunction
    // ...
    text123Content(myValue, myFunction)
)

@OptIn(ExperimentalPermissionsApi::class)
@Composable
fun test123Content(
    myValue: Int,
    myFunction: () -> Unit
) {
    // ...
)

Then update your MainActivity as follows:

@AndroidEntryPoint
class MainActivity : ComponentActivity() {


    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        setContent {
            SomeTheme {
                test123Wrapper()
            }
        }
    }
}

Now that you separated the ViewModel from the actual Composable content, you can call the test123Content Composable in your Preview and pass in data manually instead of needing a ViewModel:

@Preview(showBackground = true)
@Composable
fun GreetingPreview() {
    SomeTheme {
        test123Content(
            123,
            Log.d("ViewModel", "My ViewModel function")
        )
    }
}

This way, you can use Hilt in the normal App, but still see the layout of Composables using the Preview.