Get the same instance of ViewModel in Fragment which is defined in Activity with parameter
Asked Answered
S

2

5

So, I am using Koin for dependency injection, Here is what I did inside a activity

class ModuleDetailActivity : AppCompatActivity() {

    private lateinit var moduleId:String
    private lateinit var levelModule:Level.Module

    private val moduleViewModel: ModuleViewModel by viewModel { parameterOf(moduleId, levelModule) }

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

        ...
        ...

        moduleId = intent.getString("module_id")
        levelModule = intent.getParcelable("level_module")

        ...
        ...
    }
}

Now, I have multiple fragment which ModuleDetailActivity can add or replace and I want the same instance of moduleViewModel in those fragments without passing any parameters inside Fragment.

class ModuleDetailFragment : Fragment() {

    private val moduleViewModel: ModuleViewModel by sharedViewModel()

    ...
    ...
}

I know this will throw a error and as expected you can see this

Caused by: org.koin.core.error.InstanceCreationException: Could not create instance for [Factory:'****.ui.module.ModuleViewModel']

This is how I have initialized the module

val viewModelModule = module {
    viewModel { (id : String, levelModule:Level.Module) -> ModuleViewModel(id, levelModule, get()) }
}

Is there any solution on how I can get the same instance of ModuleViewModel defined inside activity without passing parameter inside a Fragment?

Spiky answered 14/2, 2020 at 9:12 Comment(0)
E
10

Using KOIN

ViewModel instance can be shared between Fragments and their host Activity.

To inject a shared ViewModel in a Fragment use:

by sharedViewModel() - lazy delegate property to inject shared ViewModel instance into a property
getSharedViewModel() - directly get the shared ViewModel instance

Just declare the ViewModel only once:

val weatherAppModule = module {

   // WeatherViewModel declaration for Weather View components
   viewModel { WeatherViewModel(get(), get()) }

}

In your activity:

class WeatherActivity : AppCompatActivity() {

    /*
     * Declare WeatherViewModel with Koin and allow constructor dependency injection
     */
    private val weatherViewModel by viewModel<WeatherViewModel>()
}

In your fragment:

class WeatherHeaderFragment : Fragment() {

    /*
     * Declare shared WeatherViewModel with WeatherActivity
     */
    private val weatherViewModel by sharedViewModel<WeatherViewModel>()
}

Other fragment:

class WeatherListFragment : Fragment() {

    /*
     * Declare shared WeatherViewModel with WeatherActivity
     */
    private val weatherViewModel by sharedViewModel<WeatherViewModel>()
}
Endophyte answered 18/6, 2020 at 16:39 Comment(0)
D
0

You should initialise viewModel as below:

class ModuleDetailActivity : AppCompatActivity() {

    private val moduleId: String by lazy {
        intent.getString("module_id")
    }

    private val levelModule: Level.Module by lazy {
        intent.getParcelable("level_module")
    }

    private val moduleViewModel: ModuleViewModel by viewModel {
        parameterOf(moduleId, levelModule)
    }

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

        ...
        ...

        viewModel.doYourStuff()

        ...
        ...
    }
}

We should use get() or inject() for plain Java objects only. To inject ViewModel, they provided separate way as viewModel()

Not sure why it isn't working for you.

Dace answered 14/2, 2020 at 9:56 Comment(5)
I have updated the question and added few detail which I have missed. I understand I can get ViewModel instance with viewModel. But what I want is the same instance which if defined in activity with parameter in fragment without parameters.Spiky
Brother, I understand how I can define ViewModel inside a Activity. Is there a way I can get the same viewmodel inside a fragment without passing the parameters which i have already done in Activity?Spiky
@Spiky I have used same as you have. Not sure why it isn't working for you. I have shared code from my working example.Dace
You have mentioned only the activity part. How to do that in fragment? And I don't want to pass the same parameters in fragment.Spiky
@Spiky Fragment portion is correct, same as I have implemented.Dace

© 2022 - 2024 — McMap. All rights reserved.