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

Jetpack Compose中ViewModel与导航的状态保留问题求助

问题:Jetpack Compose中ViewModel跨页面数据丢失的原因及解决方法

我正在学习如何使用ViewModel在Jetpack Compose的不同页面间传递数据,编写了一个简单的示例程序。在PlayerSelection页面中,点击选择玩家数量的按钮后,MainViewModel中的howManyPlayers值可成功修改并正常显示,但导航至TwoPlayerGame页面后该值重置为0。

现有代码

MainActivity代码

sealed class Destination(val route: String) {
    object PlayerSelection: Destination("PlayerSelection")
    object TwoPlayerGame: Destination("TwoPlayerGame")
    object ThreePlayerGame: Destination("ThreePlayerGame")
    object FourPlayerGame: Destination("FourPlayerGame")
}


class MainActivity : ComponentActivity() {

    private val viewModel: MainViewModel by viewModels()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            Viewmodels_and_NavigationTheme {
                // A surface container using the 'background' color from the theme
                Surface(
                    modifier = Modifier.fillMaxSize(),
                    color = MaterialTheme.colorScheme.background
                ) {
                    ScreenSetup(viewModel)
                }
            }
        }
    }
}
@Composable
fun NavigationAppHost (navController: NavController, viewModel: MainViewModel) {

    NavHost(navController = navController as NavHostController, startDestination = "PlayerSelection") {
        composable(Destination.PlayerSelection.route)  { PlayerSelection(navController,MainViewModel()) }
        composable(Destination.TwoPlayerGame.route) {TwoPlayerGame(navController,MainViewModel())}
        composable(Destination.ThreePlayerGame.route) {ThreePlayerGame(navController,MainViewModel())}
        composable(Destination.FourPlayerGame.route) {FourPlayerGame(navController,MainViewModel())}
    }
}

ScreenSetup代码

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun ScreenSetup(viewModel: MainViewModel) {
    val navController = rememberNavController()
    Scaffold(
        topBar = { TopAppBar(title = {  Text(text = "Viewmodels and Navigation") }) },
        content = { padding ->
            Column(Modifier.padding(padding)) {
                NavigationAppHost(navController = navController,viewModel)
            }
        },
        bottomBar = { Text(text = "BottomBar") }
    )
}

PlayerSelection组件代码

@Composable
fun PlayerSelection(navController: NavController, viewModel: MainViewModel) {
    val HowManyPlayers: Int by viewModel.howManyPlayers.collectAsState()
    Column {
        Row {
            Text(text = "How Many Players?")
        }
        Row() {
            Button(onClick = { viewModel.Selection(2) }) {
                Text("Two Players")
            }
            Button(onClick = { viewModel.Selection(3) }) {
                Text("Three Players")
            }
            Button(onClick = { viewModel.Selection(4) }) {
                Text("Four Players")
            }
        }
        Row() {
            Text(text = "Current Value Held in MainViewModel = ${HowManyPlayers}")
        }
        Button(onClick = {
            if (HowManyPlayers == 2) {
                navController.navigate(Destination.TwoPlayerGame.route)
            }
            if (HowManyPlayers == 3) {
                navController.navigate(Destination.ThreePlayerGame.route)
            }
            if (HowManyPlayers == 4) {
                navController.navigate(Destination.FourPlayerGame.route)
            }
        }) {
            Text(text = "Click To Navigate To Next Screen")
        }
    }
}

MainViewModel代码

class MainViewModel : ViewModel() {
    var _howManyPlayers = MutableStateFlow(0)
    var howManyPlayers = _howManyPlayers.asStateFlow()
    fun Selection(players:Int) {
        _howManyPlayers.value = players
    }
}

TwoPlayerGame组件代码

@Composable
fun TwoPlayerGame(navController: NavController, viewModel: MainViewModel) {
    val howManyPlayers by viewModel.howManyPlayers.collectAsState()
    Column {
        Row(Modifier.padding(50.dp)) {
            Text(text = "Two Player Game")
        }
        Row {
            Text(text = "Value of howManyPlayers in viewmodel = ${howManyPlayers}")
        }
    }
}

问题原因

你在NavigationAppHost的每个composable路由中,都手动创建了新的MainViewModel()实例(比如PlayerSelection(navController,MainViewModel()))。这意味着每个页面使用的都是独立的ViewModel对象,而非同一个实例。当你在PlayerSelection页面修改的是第一个ViewModel实例的数据,导航到TwoPlayerGame页面时,使用的是全新的ViewModel,其howManyPlayers初始值为0,所以显示重置了。

解决方法

要实现跨页面共享ViewModel数据,需要确保所有页面使用同一个ViewModel实例。具体修改如下:

修改NavigationAppHost代码

将每个页面组件中新建MainViewModel()的代码,替换为传入的viewModel参数:

@Composable
fun NavigationAppHost (navController: NavController, viewModel: MainViewModel) {

    NavHost(navController = navController as NavHostController, startDestination = "PlayerSelection") {
        composable(Destination.PlayerSelection.route)  { PlayerSelection(navController, viewModel) }
        composable(Destination.TwoPlayerGame.route) { TwoPlayerGame(navController, viewModel) }
        composable(Destination.ThreePlayerGame.route) { ThreePlayerGame(navController, viewModel) }
        composable(Destination.FourPlayerGame.route) { FourPlayerGame(navController, viewModel) }
    }
}

补充说明

你的MainViewModel是在MainActivity中通过by viewModels()创建的,其作用域绑定到Activity生命周期。通过将这个实例层层传递给导航图中的所有页面组件,就能保证所有页面共享同一ViewModel的状态数据,修改后的数据会在所有订阅的页面中同步更新。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 08:44:51