You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.17 06:05:13