Argumente an ViewModels übergeben (Hilt)
In diesem Rezept wird gezeigt, wie Sie Navigationsargumente (Schlüssel) mithilfe von Hilt für die Abhängigkeitsinjektion an ein ViewModel übergeben.
Funktionsweise
In diesem Beispiel wird die Funktion „Assisted Injection“ von Dagger/Hilt verwendet:
- Die
ViewModelist mit@HiltViewModelannotiert und ihr Konstruktor verwendet@AssistedInject, um den Navigationsschlüssel zu empfangen (der mit@Assistedannotiert ist). - Eine
@AssistedFactory-Schnittstelle wird definiert, um dieViewModelzu erstellen. - Die zusammensetzbare Funktion
hiltViewModelwird verwendet, um dieViewModel-Instanz abzurufen. EincreationCallbackwird bereitgestellt, um den Navigationsschlüssel an die Fabrik zu übergeben, sodass er für dasViewModelverfügbar ist.
Hinweis: Die rememberViewModelStoreNavEntryDecorator wird der entryDecorators der NavDisplay hinzugefügt. So wird sichergestellt, dass ViewModel-Elemente korrekt auf die entsprechenden NavEntry-Elemente beschränkt werden, sodass für jeden eindeutigen Navigationsschlüssel eine neue ViewModel-Instanz erstellt wird.
/* * Copyright 2025 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.example.nav3recipes.passingarguments.viewmodels.hilt import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.material3.Button import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.mutableStateListOf import androidx.compose.runtime.remember import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.lifecycle.ViewModel import androidx.lifecycle.compose.dropUnlessResumed import androidx.lifecycle.viewmodel.navigation3.rememberViewModelStoreNavEntryDecorator import androidx.navigation3.runtime.entryProvider import androidx.navigation3.runtime.rememberSaveableStateHolderNavEntryDecorator import androidx.navigation3.ui.NavDisplay import com.example.nav3recipes.content.ContentBlue import com.example.nav3recipes.content.ContentGreen import com.example.nav3recipes.passingarguments.viewmodels.basic.RouteB import com.example.nav3recipes.ui.setEdgeToEdgeConfig import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.lifecycle.HiltViewModel data object RouteA data class RouteB(val id: String) @AndroidEntryPoint class HiltViewModelsActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { setEdgeToEdgeConfig() super.onCreate(savedInstanceState) setContent { val backStack = remember { mutableStateListOf<Any>(RouteA) } NavDisplay( backStack = backStack, onBack = { backStack.removeLastOrNull() }, // In order to add the `ViewModelStoreNavEntryDecorator` (see comment below for why) // we also need to add the default `NavEntryDecorator`s as well. These provide // extra information to the entry's content to enable it to display correctly // and save its state. entryDecorators = listOf( rememberSaveableStateHolderNavEntryDecorator(), rememberViewModelStoreNavEntryDecorator() ), entryProvider = entryProvider { entry<RouteA> { ContentGreen("Welcome to Nav3") { LazyColumn { items(10) { i -> Button(onClick = dropUnlessResumed { backStack.add(RouteB("$i")) }) { Text("$i") } } } } } entry<RouteB> { key -> val viewModel = hiltViewModel<RouteBViewModel, RouteBViewModel.Factory>( // Note: We need a new ViewModel for every new RouteB instance. Usually // we would need to supply a `key` String that is unique to the // instance, however, the ViewModelStoreNavEntryDecorator (supplied // above) does this for us, using `NavEntry.contentKey` to uniquely // identify the viewModel. // // tl;dr: Make sure you use rememberViewModelStoreNavEntryDecorator() // if you want a new ViewModel for each new navigation key instance. creationCallback = { factory -> factory.create(key) } ) ScreenB(viewModel = viewModel) } } ) } } } @Composable fun ScreenB(viewModel: RouteBViewModel) { ContentBlue("Route id: ${viewModel.navKey.id} ") } @HiltViewModel(assistedFactory = RouteBViewModel.Factory::class) class RouteBViewModel @AssistedInject constructor( @Assisted val navKey: RouteB ) : ViewModel() { @AssistedFactory interface Factory { fun create(navKey: RouteB): RouteBViewModel } }
Argumente an ViewModels übergeben (einfach)
In diesem Rezept wird gezeigt, wie Sie Navigationsargumente (Schlüssel) mithilfe eines benutzerdefinierten ViewModelProvider.Factory an ein ViewModel übergeben.
Funktionsweise
- Es wird ein benutzerdefiniertes
ViewModelProvider.Factoryerstellt, das den Navigationsschlüssel als Konstruktorparameter verwendet. - In der zusammensetzbaren Funktion
entrywirdviewModel(factory = ...)verwendet, um dieViewModel-Instanz zu erstellen und den aktuellen Navigationsschlüssel an die Factory zu übergeben. Dadurch wird der Navigationsschlüssel für denViewModelverfügbar.
Hinweis: Die rememberViewModelStoreNavEntryDecorator wird der entryDecorators der NavDisplay hinzugefügt. So wird sichergestellt, dass ViewModel-Elemente korrekt auf die entsprechenden NavEntry-Elemente beschränkt werden, sodass für jeden eindeutigen Navigationsschlüssel eine neue ViewModel-Instanz erstellt wird.
/* * Copyright 2025 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.example.nav3recipes.passingarguments.viewmodels.basic import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.material3.Button import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.mutableStateListOf import androidx.compose.runtime.remember import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.compose.dropUnlessResumed import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.navigation3.rememberViewModelStoreNavEntryDecorator import androidx.navigation3.runtime.entryProvider import androidx.navigation3.runtime.rememberSaveableStateHolderNavEntryDecorator import androidx.navigation3.ui.NavDisplay import com.example.nav3recipes.content.ContentBlue import com.example.nav3recipes.content.ContentGreen import com.example.nav3recipes.ui.setEdgeToEdgeConfig data object RouteA data class RouteB(val id: String) class BasicViewModelsActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { setEdgeToEdgeConfig() super.onCreate(savedInstanceState) setContent { val backStack = remember { mutableStateListOf<Any>(RouteA) } NavDisplay( backStack = backStack, onBack = { backStack.removeLastOrNull() }, // In order to add the `ViewModelStoreNavEntryDecorator` (see comment below for why) // we also need to add the default `NavEntryDecorator`s as well. These provide // extra information to the entry's content to enable it to display correctly // and save its state. entryDecorators = listOf( rememberSaveableStateHolderNavEntryDecorator(), rememberViewModelStoreNavEntryDecorator() ), entryProvider = entryProvider { entry<RouteA> { ContentGreen("Welcome to Nav3") { LazyColumn { items(10) { i -> Button(onClick = dropUnlessResumed { backStack.add(RouteB("$i")) }) { Text("$i") } } } } } entry<RouteB> { key -> // Note: We need a new ViewModel for every new RouteB instance. Usually // we would need to supply a `key` String that is unique to the // instance, however, the ViewModelStoreNavEntryDecorator (supplied // above) does this for us, using `NavEntry.contentKey` to uniquely // identify the viewModel. // // tl;dr: Make sure you use rememberViewModelStoreNavEntryDecorator() // if you want a new ViewModel for each new navigation key instance. ScreenB(viewModel = viewModel(factory = RouteBViewModel.Factory(key))) } } ) } } } @Composable fun ScreenB(viewModel: RouteBViewModel = viewModel()) { ContentBlue("Route id: ${viewModel.key.id} ") } class RouteBViewModel( val key: RouteB ) : ViewModel() { class Factory( private val key: RouteB, ) : ViewModelProvider.Factory { override fun <T : ViewModel> create(modelClass: Class<T>): T { return RouteBViewModel(key) as T } } }
Argumente an ViewModels übergeben (Koin)
In diesem Rezept wird gezeigt, wie Sie Navigationsargumente (Schlüssel) mithilfe von Koin für die Abhängigkeitsinjektion an ein ViewModel übergeben.
Funktionsweise
- Es wird ein Koin-Modul definiert, das
ViewModelbereitstellt. - Die zusammensetzbare Funktion
koinViewModelwird verwendet, um dieViewModel-Instanz abzurufen. - Der Navigationsschlüssel wird mit
parametersOf(key)an den Konstruktor vonViewModelübergeben. Dadurch wird der Navigationsschlüssel für denViewModelverfügbar.
Hinweis: Die rememberViewModelStoreNavEntryDecorator wird der entryDecorators der NavDisplay hinzugefügt. So wird sichergestellt, dass ViewModel-Elemente korrekt auf die entsprechenden NavEntry-Elemente beschränkt werden, sodass für jeden eindeutigen Navigationsschlüssel eine neue ViewModel-Instanz erstellt wird.
package com.example.nav3recipes.passingarguments.viewmodels.koin import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.material3.Button import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.mutableStateListOf import androidx.compose.runtime.remember import androidx.lifecycle.ViewModel import androidx.lifecycle.compose.dropUnlessResumed import androidx.lifecycle.viewmodel.navigation3.rememberViewModelStoreNavEntryDecorator import androidx.navigation3.runtime.entryProvider import androidx.navigation3.runtime.rememberSaveableStateHolderNavEntryDecorator import androidx.navigation3.ui.NavDisplay import com.example.nav3recipes.content.ContentBlue import com.example.nav3recipes.content.ContentGreen import com.example.nav3recipes.ui.setEdgeToEdgeConfig import org.koin.compose.KoinApplication import org.koin.compose.viewmodel.koinViewModel import org.koin.core.module.dsl.viewModelOf import org.koin.core.parameter.parametersOf import org.koin.dsl.koinConfiguration import org.koin.dsl.module data object RouteA data class RouteB(val id: String) class KoinViewModelsActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { setEdgeToEdgeConfig() super.onCreate(savedInstanceState) setContent { val backStack = remember { mutableStateListOf<Any>(RouteA) } // Koin Compose Entry point KoinApplication( configuration = koinConfiguration { modules(appModule) } ) { NavDisplay( backStack = backStack, onBack = { backStack.removeLastOrNull() }, // In order to add the `ViewModelStoreNavEntryDecorator` (see comment below for why) // we also need to add the default `NavEntryDecorator`s as well. These provide // extra information to the entry's content to enable it to display correctly // and save its state. entryDecorators = listOf( rememberSaveableStateHolderNavEntryDecorator(), rememberViewModelStoreNavEntryDecorator() ), entryProvider = entryProvider { entry<RouteA> { ContentGreen("Welcome to Nav3") { LazyColumn { items(10) { i -> Button(onClick = dropUnlessResumed { backStack.add(RouteB("$i")) }) { Text("$i") } } } } } entry<RouteB> { key -> val viewModel = koinViewModel<RouteBViewModel> { parametersOf(key) } ScreenB(viewModel = viewModel) } } ) } } } } // Local Koin Module private val appModule = module { viewModelOf(::RouteBViewModel) } @Composable fun ScreenB(viewModel: RouteBViewModel) { ContentBlue("Route id: ${viewModel.navKey.id} ") } class RouteBViewModel(val navKey: RouteB) : ViewModel()