Jetpack Compose切换导航图时HiltViewModel数据丢失求助
问题分析与解决方案
核心原因
你遇到的问题并非StateFlow本身的问题,而是SharedViewModel的作用域设置错误:如果ViewModel是在单个导航图的作用域内创建的,切换导航图时原作用域会被销毁,ViewModel实例随之重置,导致StateFlow存储的sessionId丢失。
解决方案
不需要替换为SharedFlow(StateFlow更适合存储持久化的会话状态,SharedFlow多用于一次性事件),只需调整ViewModel的作用域,确保跨导航图共享同一个实例:
1. 手动管理ViewModel作用域(无依赖注入)
将SharedViewModel的作用域绑定到MainActivity,确保整个应用生命周期内只有一个实例:
// MainActivity中初始化ViewModel class MainActivity : ComponentActivity() { private lateinit var sharedViewModel: SharedViewModel override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) sharedViewModel = ViewModelProvider(this)[SharedViewModel::class.java] setContent { YourAppTheme { val navController = rememberNavController() NavHost( navController = navController, startDestination = if (sharedViewModel.sessionId.value != null) "dashboard" else "login" ) { loginGraph(navController) dashboardGraph(navController) } } } } }
在各个屏幕中,通过MainActivity的作用域获取ViewModel:
@Composable fun LoginScreen(navController: NavController) { val sharedViewModel = viewModel<SharedViewModel>( storeOwner = LocalContext.current as MainActivity ) // 登录逻辑:调用sharedViewModel.setSessionId(...) } @Composable fun TableQueryScreen(navController: NavController) { val sharedViewModel = viewModel<SharedViewModel>( storeOwner = LocalContext.current as MainActivity ) val sessionId by sharedViewModel.sessionId.collectAsState() // 使用sessionId发起API请求 }
2. 依赖注入方式(推荐Hilt)
如果使用Hilt,只需给SharedViewModel添加注解,自动绑定到Activity作用域:
@HiltViewModel class SharedViewModel @Inject constructor() : ViewModel() { private val _sessionId = MutableStateFlow<String?>(null) val sessionId = _sessionId.asStateFlow() fun setSessionId(id: String) { _sessionId.value = id } }
在所有Compose屏幕中,直接通过hiltViewModel()获取同一个实例:
@Composable fun LoginScreen(navController: NavController) { val sharedViewModel = hiltViewModel<SharedViewModel>() // 登录逻辑 } @Composable fun TableQueryScreen(navController: NavController) { val sharedViewModel = hiltViewModel<SharedViewModel>() val sessionId by sharedViewModel.sessionId.collectAsState() // API调用逻辑 }
3. 避免导航图局部作用域
检查你的导航图配置,不要给单个导航图指定独立的viewModelStoreOwner,否则会创建隔离的ViewModel实例:
// 错误示例:每个导航图创建独立ViewModel作用域 navigation( startDestination = "login", route = "loginGraph", viewModelStoreOwner = { /* 自定义作用域 */ } ) // 正确做法:使用默认的Activity作用域 navigation(startDestination = "login", route = "loginGraph")
内容的提问来源于stack exchange,提问作者Manuel Lucas
相关产品推荐
相关产品推荐

