Jetpack Compose Navigation:PopUpTo Screen 来自除参数外具有相同路由的屏幕

Jetpack Compose Navigation: PopUpTo Screen from Screens that have same route except argument

导航组合版本2.4.0-alpha06

我有一个使用 Scaffold 的导航抽屉,部分项目是由 ViewModel 动态生成的。

示例项目是

其中 A、B、C...都共享相同的 Screen,称为 Category,只是传递的参数不同(例如 Category/A、Category/B)。

在我的Scaffold

里面
...

val items = viewModel.getDrawerItems()
// This gives something like 
// ["Home", "Category/A", "Category/B", "Category/C", ..., "Settings"] 
// where each String represents "route"

...

val backstackEntry = navController.currentBackStackEntryAsState()
val currentScreen = Screen.fromRoute(
    backstackEntry.value?.destination?.route
)
Log.d("Drawer", "currentScreen: $currentScreen")

items.forEach { item ->
    DrawerItem(
        item = item, 
        isSelected = currentScreen.name == item.route, 
        onItemClick = {
            Log.d("Drawer", "destinationRoute: ${item.route}")
            navController.navigate(item.route)
            scope.launch {
                scaffoldState.drawerState.close()
            }
        }
    )
}

这段代码工作得很好,除了当我访问主屏幕时,我想清除所有后台堆栈直到不包括主页。

我试过添加 NavOptionsBuilder

...

navController.navigate(item.route) {
    popUpTo(currentScreen.name) {
        inclusive = true
        saveState = true
    }
}
...

但是,这不起作用,因为 currentScreen.name 会给出类似 Category/{title} 的内容,而 popUpTo 仅尝试从后台查找精确匹配,因此它不会弹出任何内容。

有真正的 compose-navigation 方法可以解决这个问题吗?还是我应该将最后一个“标题”保存在 ViewModel 的某处并使用它?

This tutorial from Google 具有类似的结构,但它只是堆叠屏幕,因此从屏幕 A -> B -> A 返回并单击后退将返回到 B -> A,这不是理想的行为我。

提前谢谢你。

当您指定 popUpTo 时,您应该传递您要导航到的相同项目,在这种情况下:

navController.navigate(item.route) {
    popUpTo(item.route) {
        inclusive = true
    }
}

也不确定在这种情况下是否需要指定saveState,由您决定:

Whether the back stack and the state of all destinations between the current destination and the NavOptionsBuilder.popUpTo ID should be saved for later restoration via NavOptionsBuilder.restoreState or the restoreState attribute using the same NavOptionsBuilder.popUpTo ID (note: this matching ID is true whether inclusive is true or false).

受到@Philip Dukhov 的启发,我能够实现我想要的。

...

navController.navigate(item.route) {
    // keep backstack until user goes to Home
    if (item.route == Screen.Home.name) {
        popUpTo(item.route) {
            inclusive = true
            saveState = true
        }
    } else {
        // only restoreState for non-home screens
        restoreState = true
    }
...

不幸的是,如果我添加 launchSingleTop = true,具有不同参数的屏幕由于某种原因不会重新组合,但这可能是另一个话题。

您可以创建一个扩展函数来在所有地方提供 popUpTo 功能。

fun NavHostController.navigateWithPopUp(
    toRoute: String,  // route name where you want to navigate
    fromRoute: String // route you want from popUpTo.
) {
    this.navigate(toRoute) {
        popUpTo(fromRoute) {
            inclusive = true // It can be changed to false if you
                             // want to keep your fromRoute exclusive
        }
    }
}

用法

navController.navigateWithPopUp(Screen.Home.name, Screen.Login.name)