Get sharedviewmodel in childfragment using Koin and navArgs
Asked Answered
P

3

5

I have a fragment which basically only holds a bottomnavigation and a viewmodel. This viewmodel is shared by all the childfragments in this fragment. My parentfragment looks a bit like this:

class UserDetailFragment : Fragment() {

    private val args: UserDetailFragmentArgs by navArgs()
    private val userDetailViewModel: UserDetailViewModel by viewModel { parametersOf(args.user) }

    //standard code
    //onCreateActivity where I do some basic stuff like switching the fragments
}

The viewmodel is registered in koin like so:

val viewModelModule = module {
        /* other viewmodels */
        viewModel { (user: UserModel) -> UserDetailViewModel(get(), get(), user) }
    }

Then we have the childfragments which I'm trying to define like this:

class UserAlbumsFragment : Fragment() {

    private val userDetailViewModel: UserDetailViewModel by sharedViewModel() //THIS LINE ISN'T WORKING

    //standard code again
}

As you can see one line isn't working, I tried writing it as:

private val userDetailViewModel: UserDetailViewModel 
   by sharedViewModel(from = { parentFragment as ViewModelStoreOwner })

but I keep getting this error:

Caused by: org.koin.core.error.NoParameterFoundException: Can't get parameter value #0 from org.koin.core.parameter.DefinitionParameters@426de94
        at org.koin.core.parameter.DefinitionParameters.elementAt(DefinitionParameters.kt:31)
        at org.koin.core.parameter.DefinitionParameters.component1(DefinitionParameters.kt:33)
        at com.kvw.technicaltestmediamonks.di.KoinModules$viewModelModule$1$5.invoke(Unknown Source:11)
        at com.kvw.technicaltestmediamonks.di.KoinModules$viewModelModule$1$5.invoke(KoinModules.kt:37)
        at org.koin.core.instance.DefinitionInstance.create(DefinitionInstance.kt:54)

How do I get this viewmodel without defining the userModel again?

Pesek answered 28/11, 2019 at 17:58 Comment(0)
E
6

If you have a fragment with container and bottom navigation you can try this trick:

In your parent fragment you bind ViewModel:

private val viewModel: UserDetailViewModel by viewModel()

And after in your child fragment which contains in container of your parent fragment:

private val viewModel by lazy { requireParentFragment().getViewModel<UserDetailViewModel>() }

This ViewModel will be use viewModelStore of your parents fragment, so it will be same state viewModel for all child fragments

Koin version 2.1.0

Evasive answered 25/2, 2020 at 10:6 Comment(2)
Is that possible with parent Activity and children as fragments?Maxentia
@Maxentia For this case use standard API insert-koin.io/docs/reference/koin-android/…Evasive
S
5

I had same issue and i handled this issue by changing the viewModel to single when i registered the module in koin. In your case, try to modify with this code

val viewModelModule = module {
    /* other viewmodels */
    single { (user: UserModel) -> UserDetailViewModel(get(), get(), user) }
}

If you use viewModel instead of single, Koin will create new object when you call sharedViewModel(). So the user parameter is needed to recreate the view model.

Hope this will solves your problem.

Setser answered 26/12, 2019 at 9:1 Comment(0)
J
0

In my case I wanted 5 fragments inside viewpager2 to communicate with each other in a single ViewModel of tablayout fragment.

Previously I have declared viewmodel as Singletone on Koin module. The Fragement was getting appropriate viewmodel for the first time. But once I restart that fragment after some navigation, the viewmodel was not behaving appropriately.

After a bit of debugging, I found that viewmodelscope was not working because the vm is cleared on my previous view destroy but due to singletone pattern the same instance was given me back once I get back to same fragment.

By searching about viewmodelscope I got this issue tracker page:

https://github.com/InsertKoinIO/koin/issues/506#issuecomment-521950186

So got that, Koin is incapable for that scenario, with the current version exactly. So I tried this solution after that..

The ViewModel I am using required a repository as a constructor parameter. My Koin module declarations is as follows,

module {
    factory { SearchApi() }
    single<SearchRepo> { SearchRepoImpl(get()) } // It's dependant on SearchApi
}

I am not declaring viewmodel here in Koin DI. To get the shared vm in all fragments I used androidx.fragment delegate :

val viewmodel by activityViewModels\<SearchViewModel>()

But to create viewmodel with the repository as constructor parameter, I used Koin only.

class SearchViewModelFactory : ViewModelProvider.NewInstanceFactory(), KoinComponent {
    private val searchRepo: SearchRepo by inject()
    override fun <T : ViewModel> create(modelClass: Class<T>): T = SearchViewModel(searchRepo) as T
}

and get the shared vm at every fragment as,

val searchViewModel: SearchViewModel by activityViewModels { SearchViewModelFactory() }

At least till Koin releases a fix, this might help someone.. My current Koin-android version:

2.1.5

used Libraries:

implementation "org.koin:koin-android:$koin_version"
implementation "org.koin:koin-android-viewmodel:$koin_version"

This codelab helped in getting androidx artifact,

https://developer.android.com/codelabs/basic-android-kotlin-training-shared-viewmodel#0

Juvenility answered 17/2, 2022 at 10:34 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.