Closed TooLazyy closed 3 years ago
The recommendation is your host activity will have a view model. Then fragments can use the sharedViewModel. Koin already does that for you. Don't create your own scope. https://doc.insert-koin.io/#/koin-android/viewmodel?id=shared-viewmodel
`class WeatherActivity : AppCompatActivity() {
/*
* Declare WeatherViewModel with Koin and allow constructor dependency injection
*/
private val weatherViewModel by viewModel<WeatherViewModel>()
}
class WeatherHeaderFragment : Fragment() {
/*
* Declare shared WeatherViewModel with WeatherActivity
*/
private val weatherViewModel by sharedViewModel<WeatherViewModel>()
}
class WeatherListFragment : Fragment() {
/*
* Declare shared WeatherViewModel with WeatherActivity
*/
private val weatherViewModel by sharedViewModel<WeatherViewModel>()
}
`
Yeah, I know. But a shared viewmodel is tied to an activity's lifecycle, so it won't be destroyed with fragment. Plus in my case I have some fragment dependent logic inside its viewmodel, that wouldn't be nice to separate it and put just 1 call to a shared view model. In Dagger2 I can get a parent Component and inject fragment viewmodel inside my dialog, so now I try Koin and wanna find the way to do the same.
You need only declare sharedViewModel
inside fragments.
Don't create your own scope.
class MyFragment : Fragment() {
private val myViewModel by sharedViewModel<MyViewModel>()
}
The extension sharedViewModel
call getSharedViewModel
that uses requireActivity()
internally and already "bind" the scope to activity.
inline fun <reified T : ViewModel> Fragment.sharedViewModel(
qualifier: Qualifier? = null,
noinline parameters: ParametersDefinition? = null
): Lazy<T> =
lazy { getSharedViewModel<T>(qualifier, parameters) }
fun <T : ViewModel> Fragment.getSharedViewModel(
clazz: KClass<T>,
qualifier: Qualifier? = null,
parameters: ParametersDefinition? = null
): T {
return requireActivity().getViewModel( // Here
clazz,
qualifier,
parameters
)
}
Therefore, you don't need to declare the viewModel
within the activity if you do not use it .
You need only declare
sharedViewModel
inside fragments.Don't create your own scope.
class MyFragment : Fragment() { private val myViewModel by sharedViewModel<MyViewModel>() }
The extension
sharedViewModel
callgetSharedViewModel
that usesrequireActivity()
internally and already "bind" the scope to activity.inline fun <reified T : ViewModel> Fragment.sharedViewModel( qualifier: Qualifier? = null, noinline parameters: ParametersDefinition? = null ): Lazy<T> = lazy { getSharedViewModel<T>(qualifier, parameters) } fun <T : ViewModel> Fragment.getSharedViewModel( clazz: KClass<T>, qualifier: Qualifier? = null, parameters: ParametersDefinition? = null ): T { return requireActivity().getViewModel( // Here clazz, qualifier, parameters ) }
Therefore, you don't need to declare the
viewModel
within the activity if you do not use it .
will that viewmodel be destroyed with fragment? I mean if I leave the fragment with declared sharedviewmodel will it still be alive?
You need only declare
sharedViewModel
inside fragments.Don't create your own scope.
class MyFragment : Fragment() { private val myViewModel by sharedViewModel<MyViewModel>() }
The extension
sharedViewModel
callgetSharedViewModel
that usesrequireActivity()
internally and already "bind" the scope to activity.inline fun <reified T : ViewModel> Fragment.sharedViewModel( qualifier: Qualifier? = null, noinline parameters: ParametersDefinition? = null ): Lazy<T> = lazy { getSharedViewModel<T>(qualifier, parameters) } fun <T : ViewModel> Fragment.getSharedViewModel( clazz: KClass<T>, qualifier: Qualifier? = null, parameters: ParametersDefinition? = null ): T { return requireActivity().getViewModel( // Here clazz, qualifier, parameters ) }
Therefore, you don't need to declare the
viewModel
within the activity if you do not use it .will that viewmodel be destroyed with fragment? I mean if I leave the fragment with declared sharedviewmodel will it still be alive?
You need only declare
sharedViewModel
inside fragments.Don't create your own scope.
class MyFragment : Fragment() { private val myViewModel by sharedViewModel<MyViewModel>() }
The extension
sharedViewModel
callgetSharedViewModel
that usesrequireActivity()
internally and already "bind" the scope to activity.inline fun <reified T : ViewModel> Fragment.sharedViewModel( qualifier: Qualifier? = null, noinline parameters: ParametersDefinition? = null ): Lazy<T> = lazy { getSharedViewModel<T>(qualifier, parameters) } fun <T : ViewModel> Fragment.getSharedViewModel( clazz: KClass<T>, qualifier: Qualifier? = null, parameters: ParametersDefinition? = null ): T { return requireActivity().getViewModel( // Here clazz, qualifier, parameters ) }
Therefore, you don't need to declare the
viewModel
within the activity if you do not use it .will that viewmodel be destroyed with fragment? I mean if I leave the fragment with declared sharedviewmodel will it still be alive?
The lifecycleOwner
in this case is the Activity. The viewModel will be destroyed with the Activity and not with fragment.
So here we're back again. As I told before - I don't need a sharedviewmodel for the case described above. I need to get my fragment's NOT shared view model from a dialog. In case of shared vm it will outlive fragment, what's not good in my case. So as I can see there's no way to get NOT shared vm from a child fragment event if you have the scopeId.
So here we're back again. As I told before - I don't need a sharedviewmodel for the case described above. I need to get my fragment's NOT shared view model from a dialog. In case of shared vm it will outlive fragment, what's not good in my case. So as I can see there's no way to get NOT shared vm from a child fragment event if you have the scopeId.
For your case, I think you could use [getParentFragment](https://developer.android.com/reference/androidx/fragment/app/Fragment.html#getParentFragment()) from DialogFragment
as a LifecycleOwner
and get viewModel from that. Maybe this...
// on dialog
val myViewModel: MyViewModel = getParentFragment().getViewModel(...) // using named here
What do you think?
yeah, guess that's the only way
You can try to use this solution:
private val viewModel by lazy {
requireParentFragment().getViewModel<MyViewModel>()
}
Or if you are using Navigation component from Google:
inline fun <reified VM : ViewModel> Fragment.sharedGraphViewModel(
@IdRes navGraphId: Int,
qualifier: Qualifier? = null,
noinline parameters: ParametersDefinition? = null
) = lazy {
val store = findNavController().getViewModelStoreOwner(navGraphId).viewModelStore
getKoin().getViewModel(ViewModelParameter(VM::class, qualifier, parameters, null, store, null))
}
For correct work you have to put your fragments to the nested navigation
Yo can use this too
scope(named
and in your YourFragmentClass just add extended with AndroidScopeComponent and create
override val scope : Scope by fragmentScope()
and that's all
Can you share please the code of how to get the viewmodel in the base and child class and why you are using named() here
Let's say I have a module for my fragment.
From
MyFragmentVM
I open a dialog fragment, where I passMyFragment
's scopeId. From my dialog on some action I would like to call myMyFragmentVM's
method. Is there any way to get theMyFragmentVM
? I can see some methods, but they requireViewModelParameters
and so on. I need smth that does not require anything else but scope/scopeId.