Mark
10/15/2025, 1:04 PMMainActivity which I currently access using:
inline fun <reified T : ViewModel> koinActivityViewModel(
qualifier: Qualifier? = null,
key: String? = null,
scope: Scope = currentKoinScope(),
noinline parameters: ParametersDefinition? = null,
): T = koinViewModel<T>(
qualifier = qualifier,
viewModelStoreOwner = LocalActivity.current as ComponentActivity,
key = key,
scope = scope,
parameters = parameters,
)
Claude is recommending I just use single { } for such a view model. Thoughts?Sergey Dmitriev
10/16/2025, 7:03 AMsingle for view models, they should init and be cleared in respect with their owner lifecycle (activity in your case)arnaud.giuliani
10/16/2025, 7:45 AMMark
10/16/2025, 7:48 AMSergey Dmitriev
10/16/2025, 7:51 AMviewModel { } specifically for ViewModelsMark
10/16/2025, 7:54 AMSergey Dmitriev
10/16/2025, 7:57 AMscoped to the screenWhat do you mean by “screen”? A NavBackStackEntry?
Mark
10/16/2025, 7:59 AMMark
10/16/2025, 7:59 AMSergey Dmitriev
10/16/2025, 8:01 AMMark
10/16/2025, 8:07 AMMark
10/16/2025, 10:12 AM@Composable
inline fun <reified T : ViewModel> koinNavGraphViewModel(
navController: NavController,
qualifier: Qualifier? = null,
noinline parameters: ParametersDefinition? = null,
): T {
// Get the parent nav graph route automatically
val navGraphRoute = remember {
navController.currentBackStackEntry?.destination?.parent?.route
?: error("No parent navigation graph found")
}
val backStackEntry = navController.getBackStackEntry(navGraphRoute)
return koinViewModel(
viewModelStoreOwner = backStackEntry,
qualifier = qualifier,
parameters = parameters,
)
}Mark
10/16/2025, 11:31 AM@Composable
fun AppScreen() {
val appViewModelStoreOwner = remember {
object : ViewModelStoreOwner {
override val viewModelStore = ViewModelStore()
}
}
DisposableEffect(Unit) {
onDispose {
appViewModelStoreOwner.viewModelStore.clear()
}
}
CompositionLocalProvider(LocalAppViewModelStoreOwner provides appViewModelStoreOwner) {
// screen content
}
}
// CompositionLocal
val LocalAppViewModelStoreOwner = staticCompositionLocalOf<ViewModelStoreOwner?> { null }
@Composable
inline fun <reified T : ViewModel> koinAppViewModel(
qualifier: Qualifier? = null,
key: String? = null,
scope: Scope = currentKoinScope(),
noinline parameters: ParametersDefinition? = null,
): T {
return koinViewModel(
qualifier = qualifier,
viewModelStoreOwner = LocalAppViewModelStoreOwner.current
?: error("No ViewModelStoreOwner provided. Make sure AppScreen provides it."),
key = key,
scope = scope,
parameters = parameters,
)
}Sergey Dmitriev
10/16/2025, 2:23 PMviewModelStoreOwner explicitly?
Isn’t it enought to just
@Composable
fun App() {
AppTheme {
// If you want to share a view model just instantiate it on the higher level
// it will be attached to Activity or main UIController
val sharedViewModel = koinViewModel<SharedViewModel>()
val navController = rememberNavController()
NavHost(
navController = navController,
startDestination = MainNavKey,
modifier = Modifier.fillMaxSize(),
) {
composable<MainNavKey> {
// Then "narrower" view models you instantiate within navigation composable
val mainViewModel = koinViewModel<MainViewModel>()
MainScreen(mainViewModel)
}
}
}
}Sergey Dmitriev
10/16/2025, 2:25 PMMark
10/16/2025, 2:49 PMkoinViewModel in the screen where it’s needed, because of the incorrect viewmodelstoreowner. This would be an easy mistake to make.Mark
10/17/2025, 5:25 AMval LocalAppViewModelStoreOwner = staticCompositionLocalOf<ViewModelStoreOwner?> { null }
@Composable
inline fun <reified T : ViewModel> koinAppViewModel(
qualifier: Qualifier? = null,
key: String? = null,
scope: Scope = currentKoinScope(),
noinline parameters: ParametersDefinition? = null,
): T {
val appViewModelStoreOwner = LocalAppViewModelStoreOwner.current
?: error("No ViewModelStoreOwner provided. Make sure AppScreen provides it.")
return koinViewModel(
qualifier = qualifier,
viewModelStoreOwner = appViewModelStoreOwner,
key = key,
scope = scope,
parameters = parameters,
)
}
@Composable
fun AppScreen() {
CompositionLocalProvider(
LocalAppViewModelStoreOwner provides LocalViewModelStoreOwner.current,
) {
// content
}
}
@Composable
fun FooScreen() {
val fooViewModel: FooViewModel = koinAppViewModel()
// content
}Mark
10/21/2025, 4:37 AMretain api could be used for this. https://proandroiddev.com/exploring-retain-api-a-new-way-to-persist-state-in-jetpack-compose-bfb2fe2eae43 Is this something koin would work with?arnaud.giuliani
10/21/2025, 11:48 AM