NavOptionsBuilder.launchSingleTop 是否适用于 Jetpack Compose 中的嵌套导航图?

Does NavOptionsBuilder.launchSingleTop work with nested navigation graphs in Jetpack Compose?

我在 Jetpack Compose 中使用 BottomNavigationnavigation-compose:2.5.0-alpha04,我想用 nested navigation 封装每个选项卡的流程。为此,我创建了一个包含不同图形的 NavHost:

    NavHost(
        navController = navController,
        startDestination = defaultTab.route
    ) {
        eventsGraph()

        employeesGraph()

        devicesGraph()

        feedbackGraph()
    }

每个图在 navigation 扩展函数中包含自己的 composable

正如 docs 所建议的那样,当 BottomNavigationItem 被点击时,我正在按以下方式配置我的 NavOptions

onClick = {
    navController.navigate(tab.route) {
        popUpTo(navController.graph.findStartDestination().id) {
            saveState = true
        }
        launchSingleTop = true
        // We want to reset the graph if it is clicked while already selected
        restoreState = tab != currentTab
    }
    currentTab = tab
}

我希望此代码忽略对已选定项目的点击,并始终将任何选定的选项卡作为导航图的根,即如果用户位于任何给定选项卡的根目的地,则在返回时退出应用程序。

相反,在其起始目的地的默认选项卡上的每次单击都会将同一目的地的另一个实例放入堆栈中,而在非默认选项卡上按回键会使我回到 defaultTab 的目的地.

我尝试弹出目的地 inclusive,但 NavController 不出所料,就是找不到已保存的 Destination 的 ID 来恢复状态。我尝试 saveState 恢复它的相同 tab != currentTab 条件,但它似乎没有效果。

是否可以通过导航 API 实现所需的行为,还是我需要自己处理?

更新

我最终想出了以下重置当前选项卡图表的解决方案。基本上,我们不是使用 popUpTo 选项导航,而是将堆栈弹出到起始目的地。

onClick = {
    if (tab == currentTab) {
        navController.popBackStack(
            route = tab.startDestination,
            inclusive = false
        )
        return@BottomNavigationItem
    }
    /* ... */
}

defaultTab 的图形作为根仍然存在问题,这意味着在任何非默认选项卡的启动时按下后退按钮不会退出应用程序。

简答:是的,你需要自己写这个逻辑。幸运的是,大部分由导航库处理。
要求:

  1. 堆栈中一次一个选项卡
  2. 选项卡的导航状态在离开时保存并在进入时恢复
  3. 所选选项卡的导航状态在点击时重置
  4. 任何选定的选项卡都是导航图的根

除 4 项外,其余均已达成。由于 ianhanniballake ,按系统后退键退出应用程序必须始终发生在应用程序的启动目的地,因此我保留了默认行为。

至于第 1-3 项,这里是 BottomNavigationItem 的完整 onClick 实施:

onClick = {
    // Handling resetting current tab's state
    if (tab == currentTab) {
        navController.popBackStack(
            route = tab.startDestination,
            inclusive = false
        )
        return@BottomNavigationItem
    }
    
    // Handling singleTop behavior with saving state
    navController.navigate(tab.route) {
        popUpTo(navController.graph.findStartDestination().id) {
            saveState = true
        }
        launchSingleTop = true
        restoreState = tab != currentTab
    }
    appBarViewModel.setCurrentTab(tab)
}

这有什么棘手的是,当按系统后退后显示 defaultTab 时,以前实现的 currentTab 不会更新,所以我正在根据当前导航返回堆栈作为侧面更新它效果:

val navBackStackEntry by navController.currentBackStackEntryAsState()
LaunchedEffect(navBackStackEntry) {
    // This condition is possible to be true if a system "Back" press was detected in a non-default tab,
    // navigating the user to app's start destination.
    // To correctly reflect that in bottom navigation, this code is needed
    if (navBackStackEntry?.destination?.route == appBarViewModel.defaultTab.startDestination)
        appBarViewModel.setCurrentTab(appBarViewModel.defaultTab)
}