问题描述
我有一个用 jetpack compose 制作的应用程序运行良好,直到我升级了 compose 导航库从版本 2.4.0-alpha07 到版本 2.4.0-alpha08在 alpha08 版本中,在我看来 NavBackStackEntry
类的 arguments
属性是一个 val
,所以它不能像我们一样重新分配在 2.4.0-alpha07 版本中做了.2.4.0-alpha08版本如何解决这个问题?
I had an app made with jetpack compose that worked fine until I upgraded the compose navigation libraryfrom version 2.4.0-alpha07 to version 2.4.0-alpha08In the alpha08 version it seems to me that the arguments
attribute of the NavBackStackEntry
class is a val
, so it can't be reassigned as we did in the 2.4.0-alpha07 version.How to solve this problem in version 2.4.0-alpha08?
我的导航组件是这样的:
My navigation component is this:
@Composable
private fun NavigationComponent(navController: NavHostController) {
NavHost(navController = navController, startDestination = "home") {
composable("home") { HomeScreen(navController) }
composable("details") {
val planet = navController
.previousBackStackEntry
?.arguments
?.getParcelable<Planet>("planet")
planet?.let {
DetailsScreen(it, navController)
}
}
}
}
我尝试使导航发生在详细信息页面的部分在此功能中:
The part where I try to make the navigation happen to the details page is in this function:
private fun navigateToPlanet(navController: NavHostController, planet: Planet) {
navController.currentBackStackEntry?.arguments = Bundle().apply {
putParcelable("planet", planet)
}
navController.navigate("details")
}
我已经尝试使用 apply
简单地应用到 navigateToPlanet
函数的重复 arguments
但它不起作用,屏幕打开空白,没有任何信息.这是尝试失败的代码:
I've already tried simply applying to the recurring arguments
of the navigateToPlanet
function using apply
but it doesn't work, the screen opens blank without any information. This is the code for my failed attempt:
private fun navigateToPlanet(navController: NavHostController, planet: Planet) {
navController.currentBackStackEntry?.arguments?.apply {
putParcelable("planet", planet)
}
navController.navigate("details")
}
推荐答案
根据 导航文档:
注意:通过参数传递复杂的数据结构被认为是一种反模式.每个目的地都应负责根据最少的必要信息(例如项目 ID)加载 UI 数据.这简化了流程重新创建并避免了潜在的数据不一致.
您根本不应该将 Parcelables 作为参数传递,并且从来都不是推荐的模式:不在 Navigation 2.4.0-alpha07 中,也不在 Navigation 2.4.0-alpha08 中.相反,您应该从单一的事实来源读取数据.在您的情况下,这是您的 Planet.data
静态数组,但通常是 存储层,负责为您的应用加载数据.
You shouldn't be passing Parcelables at all as arguments and never has been a recommended pattern: not in Navigation 2.4.0-alpha07 nor in Navigation 2.4.0-alpha08. Instead, you should be reading data from a single source of truth. In your case, this is your Planet.data
static array, but would normally be a repository layer, responsible for loading data for your app.
这意味着您应该传递给 DetailsScreen
的不是 Planet
本身,而是定义如何检索 Planet
的唯一键代码>对象.在您的简单情况下,这可能只是所选行星的索引.
This means what you should be passing through to your DetailsScreen
is not a Planet
itself, but the unique key that defines how to retrieve that Planet
object. In your simple case, this might just be the index of the selected Planet.
按照使用参数导航的指南,这意味着您的图表将如下所示:
By following the guide for navigating with arguments, this means your graph would look like:
@Composable
private fun NavigationComponent(navController: NavHostController) {
NavHost(navController = navController, startDestination = HOME) {
composable(HOME) { HomeScreen(navController) }
composable(
"$DETAILS/{index}",
arguments = listOf(navArgument("index") { type = NavType.IntType }
) { backStackEntry ->
val index = backStackEntry.arguments?.getInt("index") ?: 0
// Read from our single source of truth
// This means if that data later becomes *not* static, you'll
// be able to easily substitute this out for an observable
// data source
val planet = Planet.data[index]
DetailsScreen(planet, navController)
}
}
}
根据导航撰写测试指南,您不应该将您的 NavController
向下传递到您的层次结构 - 此代码无法轻松测试,并且您不能使用 @Preview
来预览您的组合.相反,您应该:
As per the Testing guide for Navigation Compose, you shouldn't be passing your NavController
down through your hierarchy - this code cannot be easily tested and you can't use @Preview
to preview your composables. Instead, you should:
- 仅将解析的参数传递到您的可组合中
- 传递应该由可组合触发的 lambda 来导航,而不是 NavController 本身.
因此您根本不应该将 NavController
传递给 HomeScreen
或 DetailsScreen
.您可以通过首先更改您在 PlanetCard
中使用它来使您的代码更具可测试性,这应该采用 lambda,而不是 NavController
:
So you shouldn't be passing your NavController
down to HomeScreen
or DetailsScreen
at all. You might start this effort to make your code more testable by first changing your usage of it in your PlanetCard
, which should take a lambda, instead of a NavController
:
@Composable
private fun PlanetCard(planet: Planet, onClick: () -> Unit) {
Card(
elevation = 4.dp,
shape = RoundedCornerShape(15.dp),
border = BorderStroke(
width = 2.dp,
color = Color(0x77f5f5f5),
),
modifier = Modifier
.fillMaxWidth()
.padding(5.dp)
.height(120.dp)
.clickable { onClick() }
) {
...
}
}
这意味着你的 PlanetList
可以写成:
This means your PlanetList
can be written as:
@Composable
private fun PlanetList(navController: NavHostController) {
LazyColumn {
itemsIndexed(Planet.data) { index, planet ->
PlanetCard(planet) {
// Here we pass the index of the selected item as an argument
navController.navigate("${MainActivity.DETAILS}/$index")
}
}
}
}
您可以看到在层次结构中继续使用 lambda 将如何帮助将您的 MainActivity
常量单独封装在该类中,而不是将它们分散到您的代码库中.
You can see how continuing to use lambdas up the hierarchy would help encapsulate your MainActivity
constants in that class alone, instead of spreading them across your code base.
通过改用索引,您可以避免创建第二个事实来源(您的参数本身),而是让自己编写可测试的代码,以支持静态数据集之外的进一步扩展.
By switching to using an index, you've avoiding creating a second source of truth (your arguments themselves) and instead set yourself up to write testable code that will support further expansion beyond a static set of data.
这篇关于如何使用新版本的撰写导航传递 Parcelable 参数?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!