代码之家  ›  专栏  ›  技术社区  ›  F.Mysir

Jetpack使用Kotlin DSL编写导航传递自定义对象

  •  0
  • F.Mysir  · 技术社区  · 4 月前

    我正在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=[...]
    

    我的viewmodel抛出了一个错误:

      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)
    

    我在viewmodel中的代码如下:

    @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
    

    所以问题是我错过了什么。。。

    1 回复  |  直到 4 月前
        1
  •  1
  •   ianhanniballake    4 月前

    savedState.toRoute 需要a typeMap -没有这个 类型地图 ,它不知道如何转换 SavedStateHandle 回到你的对象中,这样你就想传入同样的对象 类型地图 正如您在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