Background
I am creating a chat application where I need to execute logic when the user leaves a chat (a chat is just a @Composable fun
and I am using the LocalLifecycleOwner.current
combined with a ViewModel which watches the onDestroy
method to unsubscribe the user). Now that logic is also executed when the user changes tab, this should not happen.
Problem
I am using a Scaffold
with a BottomNavigation
. When I switch tabs, the old tab is destroyed. I don't want this behavior, the old tab should remain in memory. The remember
blocks are also re-executed when coming back to the tab, I don't want this. Should I use multiple navigation hosts or something?
Goal
Navigating between tabs without remember
blocks being re-executed (and also LocalLifecycleOwner.current
should not publish onDestroy
).
Sample code
The whole project can be found here: https://github.com/Jasperav/JetpackComposeNavigation. You can see when switching tabs the remember
blocks are re-executed and that the VM
is destroyed (see logging). I don't want this behavior, it should be kept in memory. This is the relevant code:
@Composable
fun Screen() {
val items = listOf(
Triple("a", Icons.Default.Person, Icons.Filled.Person),
Triple("b", Icons.Default.Notifications, Icons.Filled.Notifications),
)
var selectedTab = items[0]
val navHostController = rememberNavController()
Scaffold(
bottomBar = {
BottomNavigation {
items.forEachIndexed { index, item ->
val isSelected = index == items.indexOf(selectedTab)
BottomNavigationItem(
icon = { Icon(if (isSelected) item.second else item.third, contentDescription = null) },
label = { Text(text = item.first) },
selected = isSelected,
onClick = {
navHostController.navigate(item.first) {
popUpTo(navHostController.graph.findStartDestination().id)
launchSingleTop = true
}
}
)
}
}
}
) {
NavHost(
navHostController,
startDestination = items[0].first,
Modifier.padding(it)
) {
composable(items[0].first) {
selectedTab = items[0]
val lifecycle = LocalLifecycleOwner.current
val viewModel: ModelDontDestory = viewModel(factory = viewModelFactory {
ModelDontDestory(lifecycle)
})
remember {
println("Recomposed first")
""
}
Text("first")
}
composable(items[1].first) {
selectedTab = items[1]
val lifecycle = LocalLifecycleOwner.current
val viewModel: ModelDontDestory = viewModel(factory = viewModelFactory {
ModelDontDestory(lifecycle)
})
remember {
println("Recomposed second")
""
}
Text("Second")
}
}
}
}
inline fun <VM : ViewModel> viewModelFactory(crossinline f: () -> VM) =
object : ViewModelProvider.Factory {
override fun <T : ViewModel> create(aClass: Class<T>): T = f() as T
}
class ModelDontDestory(val lifecycle: LifecycleOwner): ViewModel(), DefaultLifecycleObserver {
init {
lifecycle.lifecycle.addObserver(this)
}
override fun onDestroy(owner: LifecycleOwner) {
super.onDestroy(owner)
println("This should never happen, this should be kept in memory")
}
}