Jetpack Compose列表项跳转对应页面异常问题排查
问题:Jetpack Compose列表项导航跳转错误,所有项均打开Screen1
点击列表中的Item 1、Item 2、Item 3均打开Screen1,但预期效果应为Item1打开Screen1、Item2打开Screen2、Item3打开Screen3,以下是实现代码:
Data Item
data class DataItem(val text: String, val imageResId: Int)
列表数据
val dataItems = listOf( DataItem("Item 1", R.drawable.concrete), DataItem("Item 2", R.drawable.plaster), DataItem("Item 3", R.drawable.blocks) )
Screen 1
@Composable fun Screen1(navController: NavController, dataItem: DataItem) { Column( modifier = Modifier .fillMaxSize() .padding(16.dp), horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.Center ) { Text(text = "Screen 1") Spacer(modifier = Modifier.height(16.dp)) Image( painter = painterResource(id = dataItem.imageResId), contentDescription = null, modifier = Modifier .size(200.dp) .clip(shape = MaterialTheme.shapes.medium) .background(MaterialTheme.colorScheme.primary) ) Spacer(modifier = Modifier.height(16.dp)) Text(text = dataItem.text) } }
Screen 2
@Composable fun Screen2(navController: NavController, dataItem: DataItem) { Column( modifier = Modifier .fillMaxSize() .padding(16.dp), horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.Center ) { Text(text = "Screen 2") Spacer(modifier = Modifier.height(16.dp)) Image( painter = painterResource(id = dataItem.imageResId), contentDescription = null, modifier = Modifier .size(200.dp) .clip(shape = MaterialTheme.shapes.medium) .background(MaterialTheme.colorScheme.primary) ) Spacer(modifier = Modifier.height(16.dp)) Text(text = dataItem.text) } }
Screen 3
@Composable fun Screen3(navController: NavController, dataItem: DataItem) { Column( modifier = Modifier .fillMaxSize() .padding(16.dp), horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.Center ) { Text(text = "Screen 3") Spacer(modifier = Modifier.height(16.dp)) Image( painter = painterResource(id = dataItem.imageResId), contentDescription = null, modifier = Modifier .size(200.dp) .clip(shape = MaterialTheme.shapes.medium) .background(MaterialTheme.colorScheme.primary) ) Spacer(modifier = Modifier.height(16.dp)) Text(text = dataItem.text) } }
数据列表页面
@Composable fun DataList(navController: NavController) { LazyColumn { items(dataItems) { item -> Box( modifier = Modifier .fillMaxWidth() .clickable { navController.navigate("screen1/${dataItems.indexOf(item)}") } ) { Row( modifier = Modifier .fillMaxWidth() .padding(16.dp), verticalAlignment = Alignment.CenterVertically ) { Image( painter = painterResource(id = item.imageResId), contentDescription = null, modifier = Modifier .size(64.dp) .clip(shape = MaterialTheme.shapes.medium) .background(MaterialTheme.colorScheme.primary) ) Spacer(modifier = Modifier.width(16.dp)) Text(text = item.text) } } } } }
导航配置
@Composable fun MyApp() { val navController = rememberNavController() NavHost(navController, startDestination = "dataList") { composable("dataList") { DataList(navController = navController) } composable( route = "screen1/{index}", arguments = listOf(navArgument("index") { type = NavType.IntType }) ) { backStackEntry -> val index = backStackEntry.arguments?.getInt("index") val dataItem = if (index != null && index < dataItems.size) dataItems[index] else dataItems[0] Screen1(navController = navController, dataItem = dataItem) } composable( route = "screen2/{index}", arguments = listOf(navArgument("index") { type = NavType.IntType }) ) { backStackEntry -> val index = backStackEntry.arguments?.getInt("index") val dataItem = if (index != null && index < dataItems.size) dataItems[index] else dataItems[0] Screen2(navController = navController, dataItem = dataItem) } composable( route = "screen3/{index}", arguments = listOf(navArgument("index") { type = NavType.IntType }) ) { backStackEntry -> val index = backStackEntry.arguments?.getInt("index") val dataItem = if (index != null && index < dataItems.size) dataItems[index] else dataItems[0] Screen3(navController = navController, dataItem = dataItem) } } }
问题定位
- 核心错误在DataList的点击事件逻辑:所有列表项的导航路径都硬编码为
"screen1/${dataItems.indexOf(item)}",无论点击哪个Item,都会跳转到Screen1对应的路由,完全没有根据Item的索引匹配Screen2、Screen3的路由。 - 额外隐患:使用
dataItems.indexOf(item)获取索引,如果列表存在重复的DataItem对象,会返回第一个匹配项的索引,导致跳转逻辑混乱。
解决方案
方案1:根据索引动态匹配目标路由
使用itemsIndexed替代items,直接获取每个Item的索引,然后根据索引拼接对应路由:
@Composable fun DataList(navController: NavController) { LazyColumn { itemsIndexed(dataItems) { index, item -> Box( modifier = Modifier .fillMaxWidth() .clickable { val targetRoute = when(index) { 0 -> "screen1/$index" 1 -> "screen2/$index" 2 -> "screen3/$index" else -> "screen1/$index" // 兜底逻辑 } navController.navigate(targetRoute) } ) { // 原有Row内容保持不变 Row( modifier = Modifier .fillMaxWidth() .padding(16.dp), verticalAlignment = Alignment.CenterVertically ) { Image( painter = painterResource(id = item.imageResId), contentDescription = null, modifier = Modifier .size(64.dp) .clip(shape = MaterialTheme.shapes.medium) .background(MaterialTheme.colorScheme.primary) ) Spacer(modifier = Modifier.width(16.dp)) Text(text = item.text) } } } } }
方案2:优化数据结构,预存目标路由(更易扩展)
修改DataItem增加目标路由字段,后续新增Item时无需修改导航逻辑:
data class DataItem(val text: String, val imageResId: Int, val targetRoute: String)
更新列表数据:
val dataItems = listOf( DataItem("Item 1", R.drawable.concrete, "screen1/0"), DataItem("Item 2", R.drawable.plaster, "screen2/1"), DataItem("Item 3", R.drawable.blocks, "screen3/2") )
简化DataList的点击事件:
@Composable fun DataList(navController: NavController) { LazyColumn { items(dataItems) { item -> Box( modifier = Modifier .fillMaxWidth() .clickable { navController.navigate(item.targetRoute) } ) { // 原有Row内容保持不变 Row( modifier = Modifier .fillMaxWidth() .padding(16.dp), verticalAlignment = Alignment.CenterVertically ) { Image( painter = painterResource(id = item.imageResId), contentDescription = null, modifier = Modifier .size(64.dp) .clip(shape = MaterialTheme.shapes.medium) .background(MaterialTheme.colorScheme.primary) ) Spacer(modifier = Modifier.width(16.dp)) Text(text = item.text) } } } } }
内容的提问来源于stack exchange,提问作者Harsh Wavikar
相关产品推荐
相关产品推荐

