https://kotlinlang.org logo
#compose
Title
# compose
t

Tgo1014

01/27/2023, 12:31 PM
I'm trying to use
ViewModelLifecycleScope {}
with
hiltViewModel()
but it fails. Is there any way I can have different instances of the same
ViewModel
? I've a pager with the same screens and I would need each of them to have independent instances of the same
ViewModel
y

yschimke

01/27/2023, 1:18 PM
I hit this also, I think a known limitation of the hiltViewModel when used inside a container like pager.
t

Tgo1014

01/27/2023, 1:34 PM
I found this issue but it has a long time without updates. The way I'm handling this right now is injecting my dependencies on the
Activity
and manually constructing instances of the `ViewModel`s passing the dependencies as parameters, it's super ugly 😕 I was following this Issue Tracker about scoping to the composable and thought this would solve the problem but apparently some Hilt work is need as well.
r

Roudy Korkis Kanaan

01/27/2023, 1:40 PM
Random plug, but I just released a navigation library with an example of how to implement a ViewPager with each page having its own lifecycle and ViewModel scope. I haven't actually tested the library with Hilt but you could theoretically actually use it just in that area of your app with a ViewPager.
t

Tgo1014

01/27/2023, 1:42 PM
@Roudy Korkis Kanaan Thanks for the suggestion. But from what I understand if I've it doesn't have hilt it means I've to construct the VMs manually, which I already do, so ideally hilt would handle this scenario so I don't have pass any parameters for it. Unless I didn't understand how your solution works.
Right now this gives me different instances, the only issue is that I've to build it by hand:
Copy code
ViewModelLifecycleScope {
    val viewModel = viewModel<MusicScreenViewModel>(
        factory = MusicScreenViewModel.Factory(
            getAllPlaylistsFlowUseCase,
            getFontSizeFlowUseCase,
            changeTextSizeUseCase,
            getMusicByIdUseCase,
            getMusicByIdFlowUseCase,
            updateMusicToneUseCase,
            addMusicToPlaylistUseCase,
            changeScrollDurationUseCase
        )
    )
    MusicScreen(MusicRouteArgs(playlist.musicList[page].id), adManager, viewModel)
}
r

Roudy Korkis Kanaan

01/27/2023, 1:45 PM
Ahhh I see, sorry misunderstood your problem ! ^^
a

Albert Chang

01/27/2023, 4:46 PM
Why do you need to use
hiltViewModel()
? Shouldn’t
viewModel()
work in this case? IIRC the only difference between
viewModel()
and
hiltViewModel()
is that
hiltViewModel()
scopes the view model to the back stack entry (and enables the view model to retrieve nav arguments), which isn’t needed in your case.
y

yschimke

01/27/2023, 5:06 PM
Surely the drive behind
hiltViewModel
is your want dependencies injected without passing them around? viewModel() does support back stack and nav arguments (through saved state handle) IIRC.
a

Albert Chang

01/27/2023, 6:00 PM
viewModel()
does support hilt view models as long as you annotated the activity or the fragment with
@AndroidEntryPoint
. See https://developer.android.com/jetpack/compose/libraries#hilt.
The function names are indeed confusing.
i

Ian Lake

01/27/2023, 6:55 PM
If you look at
viewModel
, it takes four parameters: 1. The
viewModelStoreOwner
- this controls the scope of the ViewModel. 2. A
key
which what determines the uniqueness of the ViewModel in its store (this defaults to the class name of the ViewModel, which is why when you ask for a ViewModel with the same class, you get the same instance back) 3. A
factory
which controls how the ViewModel is instantiated. If you don't define one, the
defaultViewModelFactory
from the
viewModelStoreOwner
is used 4. The
extras
which control what information, such as arguments, are passed to your
factory
. If you don't define one, the
defaultViewModelCreationExtras
from the
viewModelStoreOwner
are used. So if the
viewModelStoreOwner
is your
@AndroidEntryPoint
annotated activity or fragment, then that annotation is already generating the code that sets the correct
defaultViewModelFactory
and
defaultViewModelCreationExtras
for you. All
hiltViewModel
does is literally one line - setting the correct Hilt factory for you, even if the
viewModelStoreOwner
you provide isn't an
@AndroidEntryPoint
annotated activity or fragment
It sounds like what you actually want is for
hiltViewModel
to allow overriding that
extras
parameter so you can put your own per page argument in. That plus setting a page aware
key
would certainly be enough
t

Tgo1014

01/30/2023, 8:27 AM
@Albert Chang I see in the link you posted it should work, but it always crash not being able to create the VM instance if I use
viewModel()
instead of
hiltViewModel()
@Ian Lake the link you posted to Hilt source code actually has a
key
param, which is not in the latest Hilt version. What version is that? Some unreleased version of Hilt? Edit: it's the new alpha versions of hilt compose, I'll try passing a key
Passing the key to
hiltViewModel()
in the latest alpha provides injected VMs without me having to pass the args manually. Thanks everyone for the discussion and the help here! 🎉
Copy code
val viewModel = hiltViewModel<MusicScreenMD3ViewModel>(key = music.id.toString())
12 Views