繁体   English   中英

Jetpack Compose Navigation - 将参数传递给 startDestination

[英]Jetpack Compose Navigation - pass argument to startDestination

我正在构建的应用程序使用带路线的组合导航。 挑战在于起始目的地是动态的。

这是一个最小的例子:

class MainActivity : ComponentActivity()
{
    override fun onCreate(savedInstanceState: Bundle?)
    {
        super.onCreate(savedInstanceState)

        setContent {
            val navController = rememberNavController()

            NavHost(
                navController = navController,
                startDestination = "dynamic/1", // doesn't work
                // startDestination = "static", // workaround
            ) {
                composable(
                    route = "dynamic/{$ARG_ID}",
                    arguments = listOf(navArgument(ARG_ID) { type = NavType.StringType }),
                ) {
                    val id = it.arguments?.getString(ARG_ID)
                    Text("dynamic route, received argument: $id!")
                }
                // part of the workaround
                // composable(
                //  route = "static",
                // ) {
                //  LaunchedEffect(this) {
                //      navController.navigate("dynamic/1")
                //  }
                // }
            }
        }
    }

    companion object
    {
        const val ARG_ID = "id"
    }
}

该应用程序崩溃

java.lang.IllegalArgumentException: navigation destination route/1 is not a direct child of this NavGraph

该问题仅在“动态”路线用作起始目的地时才存在。 这可以通过使用startDestination = "static"来验证。

虽然,“静态”路由解决方法有效,但我正在寻找没有它的解决方案,因为它有点混淆了代码,并且还在后台堆栈中创建了一个额外的条目。

-> 重现问题的完整代码示例

相关问题

编辑:

我想强调的是,原始示例过去不包含“静态”可组合项。 我只添加了“静态”可组合项以获得有效的startDestination并证明可以导航到“动态”可组合项。

更新:

即使切换到可选 arguments 的查询参数语法,提供默认值,并设置没有任何参数的起始目的地也不起作用。

以下变化

NavHost(
    navController = navController,
    startDestination = "dynamic",
) {
    composable(
        route = "dynamic?$ARG_ID={$ARG_ID}",
        arguments = listOf(navArgument(ARG_ID) { type = NavType.StringType; defaultValue = "1" }),
    ) {
        val id = it.arguments?.getString(ARG_ID)
        Text("dynamic route, received argument: $id!")
    }
}

导致异常

java.lang.IllegalArgumentException: navigation destination dynamic is not a direct child of this NavGraph

完全归功于ianhanniballake ,他在评论中向我解释了解决方案。 我将在这里展示我的代码示例的工作版本。

对我来说最大的见解是:

startDestination不能匹配模式匹配意义上的可组合route ,但它必须是完全相同的字符串

这意味着参数不能直接通过startDestination设置,而必须通过参数的defaultValue设置。

这是工作示例:

class MainActivity : ComponentActivity()
{
    override fun onCreate(savedInstanceState: Bundle?)
    {
        super.onCreate(savedInstanceState)

        setContent {
            val navController = rememberNavController()

            NavHost(
                navController = navController,
                // 1st change: Set startDestination to the exact string of route
                startDestination = "dynamic/{$ARG_ID}", // NOT "dynamic/1", provide arguments via defaultValue
            ) {
                composable(
                    route = "dynamic/{$ARG_ID}",
                    // 2nd change: Set startDestination argument via defaultValue
                    arguments = listOf(navArgument(ARG_ID) { type = NavType.StringType; defaultValue = "1" }),
                ) {
                    val id = it.arguments?.getString(ARG_ID)
                    Text("dynamic route, received argument: $id!")
                }
            }
        }
    }

    companion object
    {
        const val ARG_ID = "id"
    }
}

该方法同样适用于以查询参数形式提供的参数。

老实说,我认为这是一个小限制,因为现在开始路线决定了defaultValue必须是什么。 我可能想设置一个不同的defaultValue或根本没有。 然而,在大多数情况下,这应该是最优雅的解决方案。

不应在“startDestination”NavHost 中使用动态路由值 --> navController.navigate(<dynamic route >)

所有功劳归功于ianhanniballakePeter 在我的例子中,我没有在参数数据的路由中添加任何额外的(强制键/可选键)。 我保持路线清洁,如下所示:

导航图:

 navigation(route = Route.Root.route, startDestination = Route.SubmitForm.route) {

    composable(
        route = Route.SubmitForm.route,
        arguments = listOf(
            navArgument(ARG_KEY) {
                type = NavType.StringType
                defaultValue = JsonConverter.toJson(user, User::class.java)
            },
        )
    )
}

路由封class:

sealed class Route(val route: String) {
    object MyRoute : Route("$ROOT/submit-form")
}

在视图 model 中,只需获取如下数据:

@HiltViewModel
class MyViewModel @Inject constructor(
    stateHandle: SavedStateHandle,
) : ViewModel {

    lateinit var user

    init {
        user = stateHandle.get<String>(ARG_NAME) // Supported data types
    }
}

它对我有用。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM