In my application, there are two nested navigations:
- SignedOut
- SignedIn
Every time a user navigates from SignedOut to SignedIn, a new instance of SharedViewModel must be provided.
The same instance of SharedViewModel must be shared between composables inside SignedIn.
The whole point of making SharedViewModel is that the app will be able to cache a massive amount of data between multiple composables avoiding fetching the same data several times.
What I tried:
ViewModel:
@HiltViewModel
class SharedViewModel @Inject constructor(): ViewModel() {
private val instance =
this.toString().substringAfterLast("@")
val data = instance
}
Module responsible for providing SharedViewModel:
@Module
@InstallIn(ViewModelComponent::class)
class SharedViewModelModule {
@Provides
@ViewModelScoped
fun provideSharedViewModel(): SharedViewModel =
SharedViewModel()
}
MainActivity.kt
@AndroidEntryPoint
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContent {
val navigationController = rememberNavController()
NavHost(
navController = navigationController,
startDestination = "WelcomeScreen"
) {
navigation(route = "SignedOut", startDestination = "WelcomeScreen") {...}
navigation(route = "SignedIn", startDestination = "HomeScreen") {
composable(
route = "HomeScreen",
content = {
val viewModel = hiltViewModel<SharedViewModel>(it)
}
)
composable(
route = "SecondScreen",
content = {
val viewModel =hiltViewModel<SharedViewModel>(it)
}
)
}
}
}
}
}
What I expected:
I navigate from SignedOut to SignedIn: The same instance of SharedViewModel should be provided to both HomeScreen and SecondScreen composables.
I navigate from SignedIn to SignedOut and back to SignedIn: A completely new instance should be provided for HomeScreen and SecondScreen.
Actual result:
Dagger-hilt provides two different instances for HomeScreen and SecondScreen.
In my application, there are two nested navigations:
- SignedOut
- SignedIn
Every time a user navigates from SignedOut to SignedIn, a new instance of SharedViewModel must be provided.
The same instance of SharedViewModel must be shared between composables inside SignedIn.
The whole point of making SharedViewModel is that the app will be able to cache a massive amount of data between multiple composables avoiding fetching the same data several times.
What I tried:
ViewModel:
@HiltViewModel
class SharedViewModel @Inject constructor(): ViewModel() {
private val instance =
this.toString().substringAfterLast("@")
val data = instance
}
Module responsible for providing SharedViewModel:
@Module
@InstallIn(ViewModelComponent::class)
class SharedViewModelModule {
@Provides
@ViewModelScoped
fun provideSharedViewModel(): SharedViewModel =
SharedViewModel()
}
MainActivity.kt
@AndroidEntryPoint
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContent {
val navigationController = rememberNavController()
NavHost(
navController = navigationController,
startDestination = "WelcomeScreen"
) {
navigation(route = "SignedOut", startDestination = "WelcomeScreen") {...}
navigation(route = "SignedIn", startDestination = "HomeScreen") {
composable(
route = "HomeScreen",
content = {
val viewModel = hiltViewModel<SharedViewModel>(it)
}
)
composable(
route = "SecondScreen",
content = {
val viewModel =hiltViewModel<SharedViewModel>(it)
}
)
}
}
}
}
}
What I expected:
I navigate from SignedOut to SignedIn: The same instance of SharedViewModel should be provided to both HomeScreen and SecondScreen composables.
I navigate from SignedIn to SignedOut and back to SignedIn: A completely new instance should be provided for HomeScreen and SecondScreen.
Actual result:
Dagger-hilt provides two different instances for HomeScreen and SecondScreen.
Share Improve this question edited Nov 20, 2024 at 22:02 Marcel asked Nov 20, 2024 at 21:56 MarcelMarcel 276 bronze badges 1- 1 "the app will be able to cache a massive amount of data between multiple composables avoiding fetching the same data several times" -- often, that is a better task for your repository, rather than a viewmodel. – CommonsWare Commented Nov 20, 2024 at 22:28
2 Answers
Reset to default 1Please have a look at the Android ViewModel Cheatsheet:
As you can see denoted in the orange box, a ViewModel in Hilt by default is scoped to the closest ViewModelStoreOwner
, which in your case is a BackStackEntry
in your NavGraph
. However, you can obtain a ViewModel by its BackStackEntry
and use it in other destinations.
Please try the following code:
composable(
route = "HomeScreen",
content = {
val viewModel = hiltViewModel<SharedViewModel>(it)
}
)
composable(
route = "SecondScreen",
content = {
val homeEntry = remember(backStackEntry) {
navController.getBackStackEntry("HomeScreen")
}
val sharedViewModel = hiltViewModel<ParentViewModel>(homeEntry)
}
)
This case use activityViewModels. It Returns a property delegate to access parent activity's ViewModel, if factoryProducer is specified then ViewModelProvider.Factory returned by it will be used to create ViewModel first time.
First in your build.gradle add
val fragment_version = "1.8.5"
// Kotlin
implementation("androidx.fragment:fragment-ktx:$fragment_version")
In your screen level or fragments
// Access shared ViewModel within composables
val sharedViewModel: MySharedViewModel by activityViewModels()