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

Jetpack Compose跨页面传值问题:ViewModel跳转后值为空

解决Jetpack Compose多页面ViewModel数据丢失问题

问题根源

你遇到的问题本质是ViewModel生命周期与导航页面绑定:默认通过viewModel()或hiltViewModel()获取的ViewModel,生命周期和当前导航目的地的NavBackStackEntry绑定。当你跳转到最后一页时,如果前两个页面的条目被从回退栈移除(比如使用popUpTo并设置inclusive = true),对应的NavBackStackEntry会被销毁,ViewModel也会随之清除,数据自然为空。

解决方案

方案1:使用共享ViewModel(推荐)

创建一个绑定到父级生命周期(如宿主Activity或根导航图)的共享ViewModel,让三个页面共用同一个实例,避免页面跳转时ViewModel被销毁。

步骤:

  1. 创建包含所有页面所需数据的共享ViewModel:
class SharedQrCreateViewModel : ViewModel() {
    // 第一页数据状态
    val page1Data = mutableStateOf("")
    // 第二页数据状态
    val page2Data = mutableStateOf("")
    // 可添加其他共享状态或业务方法
}
  1. 在三个页面中获取同一个共享ViewModel实例:

    • 若使用Hilt,先给ViewModel添加@HiltViewModel注解,再通过宿主Activity获取实例:
    val sharedViewModel = hiltViewModel<SharedQrCreateViewModel>(
        viewModelStoreOwner = LocalContext.current as AppCompatActivity
    )
    
    • 不使用Hilt时,通过宿主的ViewModelStoreOwner获取:
    val activityStoreOwner = LocalContext.current as ViewModelStoreOwner
    val sharedViewModel = viewModel<SharedQrCreateViewModel>(activityStoreOwner)
    
  2. 修改前两个页面的逻辑,将数据存入共享ViewModel:
    比如第一页输入完成后执行sharedViewModel.page1Data.value = inputText,第二页同理更新page2Data。最后一页直接读取这两个状态值即可。

方案2:导航时传递数据

如果数据结构简单,可直接通过导航组件的参数传递数据到最后一页,无需依赖前两个页面的ViewModel。

步骤:

  1. 在导航图中定义带参数的目的地:
fun NavGraphBuilder.qrCreateGraph(navController: NavController) {
    composable("qr_create_1") {
        QrCreate1Screen(navController)
    }
    composable(
        "qr_create_2/{page1Data}",
        arguments = listOf(navArgument("page1Data") { type = NavType.StringType })
    ) { backStackEntry ->
        val page1Data = backStackEntry.arguments?.getString("page1Data") ?: ""
        QrCreate2Screen(navController, page1Data)
    }
    composable(
        "qr_create_final/{page1Data}/{page2Data}",
        arguments = listOf(
            navArgument("page1Data") { type = NavType.StringType },
            navArgument("page2Data") { type = NavType.StringType }
        )
    ) { backStackEntry ->
        val page1Data = backStackEntry.arguments?.getString("page1Data") ?: ""
        val page2Data = backStackEntry.arguments?.getString("page2Data") ?: ""
        QrCreateFinalScreen(page1Data, page2Data)
    }
}
  1. 页面跳转时传递参数:
    第一页跳转到第二页:
navController.navigate("qr_create_2/${page1InputText}")

第二页跳转到最后一页:

navController.navigate("qr_create_final/${page1Data}/${page2InputText}")
  1. 最后一页直接接收参数并使用即可。

方案3:持久化临时数据

如果数据复杂且需要跨进程/重启保留,可使用DataStore或Room临时存储数据,最后一页读取后再清理。

示例(使用DataStore):

  1. 创建DataStore实例:
private val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "qr_temp_data")
  1. 定义存储键:
val PAGE1_DATA_KEY = stringPreferencesKey("page1_data")
val PAGE2_DATA_KEY = stringPreferencesKey("page2_data")
  1. 前两个页面保存数据:
context.dataStore.edit { preferences ->
    preferences[PAGE1_DATA_KEY] = inputText
}
  1. 最后一页读取数据:
val page1Data = context.dataStore.data
    .map { it[PAGE1_DATA_KEY] ?: "" }
    .collectAsState(initial = "")

val page2Data = context.dataStore.data
    .map { it[PAGE2_DATA_KEY] ?: "" }
    .collectAsState(initial = "")

注意事项

  • 优先选择方案1,符合ViewModel的设计理念,能轻松处理复杂状态和业务逻辑。
  • 若用导航参数传递复杂数据,需将数据类实现Parcelable或Serializable,并在导航参数中指定对应类型。
  • 避免使用单例ViewModel,除非明确需要全局唯一状态,否则易引发内存泄漏或状态混乱。

内容的提问来源于stack exchange,提问作者David Fernandes

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 06:32:07