Jetpack Navigation中SavedStateHandle ViewModel跨导航及栈清除状态不保留问题
问题分析与解决方案
状态无法保留的原因
- ViewModel生命周期与页面强绑定:你通过
koinNavViewModel()获取的SecondViewModel默认绑定到ScreenSecond对应的NavBackStackEntry生命周期。当调用navigateWithClearStack清除回退栈时,ScreenSecond的NavBackStackEntry会被销毁,关联的ViewModel也会被回收,导致SavedStateHandle中的状态随之丢失。 - SavedStateHandle作用范围限制:
SavedStateHandle的状态仅在其绑定的ViewModel生命周期内存活,一旦ViewModel被销毁,状态会被清除,无法跨页面跳转保留。
解决方法
要保留状态,需让SecondViewModel脱离单个页面的生命周期,绑定到更上层的导航容器(比如整个NavHost的父路由),这样即使页面被弹出回退栈,ViewModel仍能存活,SavedStateHandle中的状态也会保留。
修改后的代码示例
import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.compose.animation.AnimatedVisibility import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.material.Button import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.mutableStateOf import androidx.compose.ui.Modifier import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel import androidx.lifecycle.viewmodel.compose.SavedStateHandleSaveableApi import androidx.lifecycle.viewmodel.compose.saveable import androidx.navigation.NavController import androidx.navigation.NavHostController import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable import androidx.navigation.compose.rememberNavController import org.koin.androidx.compose.navigation.koinNavViewModel class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { NavigationScreen() } } } @Composable fun NavigationScreen(navController: NavHostController = rememberNavController()) { NavHost( navController = navController, startDestination = "ScreenFirst", route = "parentRoute" ) { composable("ScreenFirst") { Button(onClick = { navController.navigateWithClearStack("ScreenSecond") }) { Text(text = "跳转到第二个页面") } } composable("ScreenSecond") { // 将ViewModel绑定到父路由"parentRoute"的生命周期 val viewModel: SecondViewModel = koinNavViewModel(route = "parentRoute") Column(modifier = Modifier.fillMaxSize()) { Button(onClick = { viewModel.update(true) }) { Text(text = "显示返回按钮") } AnimatedVisibility(visible = viewModel.isBackButtonVisible) { Button(onClick = { navController.navigateWithClearStack("ScreenFirst") }) { Text(text = "返回第一个页面") } } } } } } fun NavController.navigateWithClearStack(screenRoute: String) { this.navigate(screenRoute) { popUpTo(currentBackStackEntry?.destination?.route ?: return@navigate) { inclusive = true } launchSingleTop = true } } class SecondViewModel(savedStateHandle: SavedStateHandle) : ViewModel() { @OptIn(SavedStateHandleSaveableApi::class) var isBackButtonVisible by savedStateHandle.saveable { mutableStateOf(false) } private set fun update(value: Boolean) { isBackButtonVisible = value } }
关键修改点
- 获取ViewModel时,使用
koinNavViewModel(route = "parentRoute"),将ViewModel的生命周期绑定到整个NavHost的父路由,而非ScreenSecond单个页面。这样即使ScreenSecond被弹出回退栈,ViewModel仍会保留,SavedStateHandle中的状态也不会丢失。 - 确保
NavHost的route参数设置为parentRoute,让Koin可以正确识别这个父作用域。
内容的提问来源于stack exchange,提问作者Compose Learner
相关产品推荐
相关产品推荐

