5

I have a view model. I am using Hilt. I can create this view model from activity, like this

val model: ProfileViewModel by viewModels()

However when I try to create this view model from a composable function

import androidx.compose.material.Button
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.res.stringResource
import androidx.lifecycle.viewmodel.compose.viewModel

@Composable
fun ProfileScreen(){
    val model: ProfileViewModel = viewModel()
    Button(onClick = {
        model.logout()
    }){
        Text(stringResource(R.string.log_out))
    }
}

I am getting error

2021-04-22 10:01:55.503 10482-10482/com.pulsariodev E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.pulsariodev, PID: 10482
    java.lang.RuntimeException: Cannot create an instance of class com.pulsario.ui.profile.ProfileViewModel
        at androidx.lifecycle.ViewModelProvider$NewInstanceFactory.create(ViewModelProvider.java:221)
        at androidx.lifecycle.ViewModelProvider$AndroidViewModelFactory.create(ViewModelProvider.java:278)
        at androidx.lifecycle.SavedStateViewModelFactory.create(SavedStateViewModelFactory.java:112)
        at androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.java:185)
        at androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.java:150)
        at androidx.lifecycle.viewmodel.compose.ViewModelKt.get(ViewModel.kt:78)
        at androidx.lifecycle.viewmodel.compose.ViewModelKt.viewModel(ViewModel.kt:63)
        at com.pulsario.ui.profile.ProfileScreenKt.ProfileScreen(ProfileScreen.kt:23)
        at com.pulsario.ui.main.MainScreenKt$MainScreen$2$1$1$1.invoke(MainScreen.kt:60)
        at com.pulsario.ui.main.MainScreenKt$MainScreen$2$1$1$1.invoke(MainScreen.kt:59)
        at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:118)
        at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:34)
        at androidx.navigation.compose.NavHostKt$NavHost$5$1$1.invoke(NavHost.kt:138)
        at androidx.navigation.compose.NavHostKt$NavHost$5$1$1.invoke(NavHost.kt:137)
        at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:109)
        at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:34)
        at androidx.compose.runtime.CompositionLocalKt.CompositionLocalProvider(CompositionLocal.kt:193)
        at androidx.compose.runtime.saveable.SaveableStateHolderImpl.SaveableStateProvider(SaveableStateHolder.kt:84)
        at androidx.navigation.compose.NavHostKt.SaveableStateProvider(NavHost.kt:150)
        at androidx.navigation.compose.NavHostKt.access$SaveableStateProvider(NavHost.kt:1)
        at androidx.navigation.compose.NavHostKt$NavHost$5$1.invoke(NavHost.kt:137)
        at androidx.navigation.compose.NavHostKt$NavHost$5$1.invoke(NavHost.kt:136)
        at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:109)
        at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:34)
        at androidx.compose.runtime.CompositionLocalKt.CompositionLocalProvider(CompositionLocal.kt:193)
        at androidx.navigation.compose.NavHostKt.NavHost(NavHost.kt:132)
        at androidx.navigation.compose.NavHostKt$NavHost$6.invoke(Unknown Source:13)
        at androidx.navigation.compose.NavHostKt$NavHost$6.invoke(Unknown Source:10)
        at androidx.compose.runtime.RecomposeScopeImpl.compose(RecomposeScopeImpl.kt:97)
        at androidx.compose.runtime.ComposerImpl.recomposeToGroupEnd(Composer.kt:2117)
        at androidx.compose.runtime.ComposerImpl.skipCurrentGroup(Composer.kt:2375)
        at androidx.compose.runtime.ComposerImpl.doCompose(Composer.kt:2517)
        at androidx.compose.runtime.ComposerImpl.recompose$runtime_release(Composer.kt:2488)
        at androidx.compose.runtime.CompositionImpl.recompose(Composition.kt:546)
        at androidx.compose.runtime.Recomposer.performRecompose(Recomposer.kt:733)
        at androidx.compose.runtime.Recomposer.access$performRecompose(Recomposer.kt:102)
        at androidx.compose.runtime.Recomposer$runRecomposeAndApplyChanges$2$2.invoke(Recomposer.kt:443)
        at androidx.compose.runtime.Recomposer$runRecomposeAndApplyChanges$2$2.invoke(Recomposer.kt:415)
        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)
2021-04-22 10:01:55.504 10482-10482/com.pulsariodev E/AndroidRuntime:     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:7386)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:980)
     Caused by: java.lang.InstantiationException: java.lang.Class<com.pulsario.ui.profile.ProfileViewModel> has no zero argument constructor
        at java.lang.Class.newInstance(Native Method)
        at androidx.lifecycle.ViewModelProvider$NewInstanceFactory.create(ViewModelProvider.java:219)
            ... 52 more

Here is the example from Android tutorial but this does not work for me.

4
  • You can't instantiate ViewModel object by constructor, to create the object you have two ways 1. by ViewModelProviders & 2. val model: ProfileViewModel by viewModels() extension provided in viewmodel-ktx lib Commented Apr 22, 2021 at 14:22
  • ViewModel bound to view lifecycle Commented Apr 22, 2021 at 14:23
  • in your case you can use this var model: ProfileViewModel = ViewModelProviders.of("activity-reference").get(ProfileViewModel::class.java) Commented Apr 22, 2021 at 14:25
  • But in this example they are doing this in the same way as I do developer.android.com/jetpack/compose/libraries#hilt Commented Apr 23, 2021 at 3:24

3 Answers 3

6

Because my composable function is created via navigation but not directly from Fragment or Activity I have to use the function

hiltViewModel()

Details in the documentation

Sign up to request clarification or add additional context in comments.

4 Comments

For some reason, in this case, the hilt cannot create a viewmodel if it contains your dependencies (provided via @Module). For example without dependencies or with SavedStateHandle dependency there is no creation error. The documentation is misleading "If ... is scoped to the navigation graph," - and if not, if you need exactly scoped to activity, then where does the error come from? Bug?
Now it's called hiltViewModel()
In case of navigation use val exampleViewModel = hiltViewModel<ExampleViewModel>()
hiltViewModel() from navigation and by viewModels of activity provide different viewmodel instances right? Do you have to pass viewmodel as argument if you want to use the same viewmodel in activity and navigation?
2

Here's my solution:

Step 1

In the Project build.gradle file, define lifecycle_version = "2.4.0-beta01"

as in

// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
    ext {
        compose_version = '1.0.1'
        lifecycle_version = "2.4.0-beta01"
    }
    // rest is the same

Important: It won't work with androidx.lifecycle:lifecycle-livedata:2.3.1 and probably below

Step 2

In the app build.gradle file, make 3 changes: (1) change to compileSdk 31, (2) change to targetSdk 31, and (3) add to dependencies the following implementation "androidx.lifecycle:lifecycle-viewmodel-compose:$lifecycle_version" and implementation "androidx.compose.runtime:runtime-livedata:$compose_version"

as in

android {
    compileSdk 31

    defaultConfig {
        minSdk 21
        targetSdk 31
        //rest is the same
    }
}

//rest is the same

dependencies {
    //...
    implementation "androidx.compose.runtime:runtime-livedata:$compose_version"
    implementation "androidx.lifecycle:lifecycle-viewmodel-compose:$lifecycle_version"
}

Important: It won't work with compileSdk 30 and/or targetSdk 30

Step 3

Make sure to import androidx.lifecycle.viewmodel.compose.*

import androidx.lifecycle.viewmodel.compose.*

Important: Must include the above import

Comments

0

I solved this using the below

class MyScreenManager(navController:NavigationController){

lateinit var viewModel:MyViewModel = HiltViewModelFactory(
            activity,
            navController.getBackStackEntry(MyScreen.route)
        ).create(MyViewModel::class.java)
}

The above class was not injected but navigated to from a composable function

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.