我正在 Kotlin DSL 和 Navigation Compose 中尝试类型安全,但看起来它没有按预期工作,以下是我的代码:
屏幕:
@Serializable
data object SelectMenuNav
@Serializable
data class SelectProductNav(
val menuSelection: MenuSelection
)
@Serializable
data class ProductDetailsNav(
val menuSelection: MenuSelection,
val product: Product
)
我想传递可组合的自定义对象
object CustomNavType {
val ProductType = object : NavType<Product>(
isNullableAllowed = false
) {
override fun get(bundle: Bundle, key: String): Product? {
return Json.decodeFromString(bundle.getString(key) ?: return null)
}
override fun parseValue(value: String): Product {
return Json.decodeFromString(Uri.decode(value))
}
override fun put(bundle: Bundle, key: String, value: Product) {
bundle.putString(key, Json.encodeToString(value))
}
override fun serializeAsValue(value: Product): String {
return Uri.encode(Json.encodeToString(value))
}
}
}
我的应用导航
@Composable
fun AppNavigation(modifier: Modifier = Modifier) {
val navController = rememberNavController()
NavHost(
navController = navController,
startDestination = SelectMenuNav
) {
composable<SelectMenuNav> {
ChooseMenuScreen(onSelection = { menuSelection ->
navController.navigate(
SelectProductNav(
menuSelection = menuSelection
)
)
})
}
composable<SelectProductNav>(
typeMap = mapOf(
typeOf<MenuSelection>() to NavType.EnumType(MenuSelection::class.java)
)
) {
val menuSelection = it.toRoute<SelectProductNav>().menuSelection
ChooseProductScreen(
menuSelection = menuSelection,
viewModel = hiltViewModel(),
onNavigateToProductDetails = { product ->
navController.navigate(
ProductDetailsNav(
product = product,
menuSelection = menuSelection
)
)
})
}
composable<ProductDetailsNav>(
typeMap = mapOf(
typeOf<Product>() to CustomNavType.ProductType,
typeOf<MenuSelection>() to NavType.EnumType(MenuSelection::class.java)
)
) { backStackEntry ->
Log.d("AppNavigation", "ProductDetailsNav: ${backStackEntry.toRoute<ProductDetailsNav>()}")
ProductDetailsScreen(
modifier = Modifier,
vm = hiltViewModel(),
navigateToEditor = {},
navigateBack = {}
)
}
}
}
尽管 Log 可以很好地显示已在可组合项之间成功传递的产品:
D ProductDetailsNav: ProductDetailsNav(menuSelection=NAVA, product=Product(id=4371, productName=[...]
我的视图模型抛出错误:
java.lang.IllegalArgumentException: Route app.xml.aionianew.navigation.ProductDetailsNav could not find any NavType for argument product of type app.xml.aionianew.database.internal.data.Product - typeMap received was {}
at androidx.navigation.serialization.RouteSerializerKt$generateNavArguments$2$1.invoke(RouteSerializer.kt:108)
at androidx.navigation.serialization.RouteSerializerKt$generateNavArguments$2$1.invoke(RouteSerializer.kt:103)
at androidx.navigation.NamedNavArgumentKt.navArgument(NamedNavArgument.kt:21)
at androidx.navigation.serialization.RouteSerializerKt.generateNavArguments(RouteSerializer.kt:103)
at androidx.navigation.SavedStateHandleKt.internalToRoute(SavedStateHandle.kt:50)
at app.xml.aionianew.screens.product_details.ViewModelProductDetails.<init>(ViewModelProductDetails.kt:1018)
at app.xml.aionianew.DaggerMyApp_HiltComponents_SingletonC$ViewModelCImpl$SwitchingProvider.get(DaggerMyApp_HiltComponents_SingletonC.java:497)
at dagger.hilt.android.internal.lifecycle.HiltViewModelFactory$2.createViewModel(HiltViewModelFactory.java:133)
我在视图模型中的代码如下:
@HiltViewModel
class ViewModelProductDetails @Inject constructor(
private val uc: UseCasesProductDetails,
savedState: SavedStateHandle
) : ViewModel() {
private val selectedProduct: Product = savedState.toRoute<ProductDetailsNav>().product
private val menuSelection:MenuSelection = savedState.toRoute<ProductDetailsNav>().menuSelection
所以问题是我错过了什么......
savedState.toRoute
also 需要一个 typeMap
- 如果没有 typeMap
,它不知道如何将 SavedStateHandle
转换回你的对象,所以你会想要传递与你相同的 typeMap
在 ViewModel 的导航图定义中使用:
private val productDetails = savedState.toRoute<ProductDetailsNav>(
typeMap = mapOf(
typeOf<Product>() to CustomNavType.ProductType,
typeOf<MenuSelection>() to NavType.EnumType(MenuSelection::class.java)
)
)
private val selectedProduct: Product = productDetails.product
private val menuSelection: MenuSelection = productDetails.menuSelection