撰写导航:单击导航栏项目时转到顶级目的地

问题描述 投票:0回答:1

我正在使用 Jetpack Compose 和 androidx-navigation-compose。

一般来说,我喜欢在底部导航选项卡之间切换时的保存/恢复状态机制。

但是,如果我位于特定选项卡的详细信息屏幕中,并且再次单击该选项卡,我希望应用程序导航到该选项卡各自的顶级目的地 - 而不是不执行任何操作。我怎样才能实现这种行为?

我描述的行为并未在 Now-in-Android 中实现。 然而,在 Compose 之前,它曾经是使用 androidx 导航设置底部栏时的默认行为。

我尝试构建一个最小的示例,可以在here找到。

这是最相关的部分:

Scaffold(
    modifier = Modifier.fillMaxSize(),
    bottomBar = {
        NavigationBar {
            bottomBarItems.forEach { item ->
                val screen = item.screen
                NavigationBarItem(
                    icon = { Icon(Icons.Filled.Favorite, contentDescription = null) },
                    label = { Text(item.title) },
                    selected = screen == currentTopLevelDestination.screen,
                    onClick = {
                        navController.navigate(screen.route) {
                            // Pop up to the start destination of the graph to
                            // avoid building up a large stack of destinations
                            // on the back stack as users select items
                            popUpTo(navController.graph.findStartDestination().id) {
                                saveState = true
                            }
                            // Avoid multiple copies of the same destination when
                            // reselecting the same item
                            launchSingleTop = true
                            // Restore state when reselecting a previously selected item

                            restoreState = true
                        }
                    }
                )
            }
        }

    }
) { paddingValues ->
    NavHost(
        navController = navController,
        startDestination = TopLevelDestination.HOME.route,
        modifier = Modifier.padding(paddingValues)
    ) {
        navigation(
            route = TopLevelDestination.HOME.route,
            startDestination = Screen.Home.route,
        ) {
            composable(Screen.Home.route) {
                Box(
                    contentAlignment = Alignment.Center,
                    modifier = Modifier.fillMaxSize(),
                ) {
                    Button(onClick = { navController.navigate(Screen.HomeDetail.route) }) {
                        Text("Go to Home Detail")
                    }
                }
            }
            composable(Screen.HomeDetail.route) {
                Box(
                    contentAlignment = Alignment.Center,
                    modifier = Modifier.fillMaxSize(),
                ) {
                    Text("Home Detail")
                }
            }
        }
        navigation(
            route = TopLevelDestination.MORE.route,
            startDestination = Screen.More.route,
        ) {
            composable(Screen.More.route) {
                Box(
                    contentAlignment = Alignment.Center,
                    modifier = Modifier.fillMaxSize(),
                ) {
                    Button(onClick = { navController.navigate(Screen.MoreDetail.route) }) {
                        Text("Go to More Detail")
                    }
                }
            }
            composable(Screen.MoreDetail.route) {
                Box(
                    contentAlignment = Alignment.Center,
                    modifier = Modifier.fillMaxSize(),
                ) {
                    Text("More Detail")
                }
            }
        }
    }
}

我想要的是以下行为:

  1. 应用程序在主屏幕中启动
  2. 导航至主页详细信息
  3. 点击主页底部导航项

-> 我想回家

android android-jetpack-compose androidx android-navigation android-navigation-graph
1个回答
0
投票

最后,我在 GitHub 示例中实现了此功能

跟踪当前选择的底部导航选项卡的最初想法是正确的。 如果导航堆栈未保存并在单击已激活的选项卡时恢复,它会起作用。

这是最相关的代码

@Composable
internal fun AppNavigationBar(
    navController: NavHostController
) {
    val currentTopLevelDestination by navController.currentTabItemAsState()

    NavigationBar {
        bottomBarItems.forEach { item ->
            val isTabAlreadySelected = item == currentTopLevelDestination
            NavigationBarItem(
                icon = { Icon(Icons.Filled.Favorite, contentDescription = null) },
                label = { Text(item.title) },
                selected = isTabAlreadySelected,
                onClick = {
                    navController.navigateToTabItem(
                        item = item,
                        restoreTabStack = !isTabAlreadySelected,
                    )
                }
            )
        }
    }
}

/**
 * Adds an [NavController.OnDestinationChangedListener] to this [NavController] and updates the
 * returned [State] which is updated as the destination changes.
 */
@Composable
private fun NavController.currentTabItemAsState(): State<TabItem> {
    val selectedItem = remember { mutableStateOf(TabItem.Home) }

    DisposableEffect(this) {
        val listener = NavController.OnDestinationChangedListener { _, destination, _ ->
            when {
                destination.hierarchy.any { it.route == TabItem.More.navGraphRoute } -> {
                    selectedItem.value = TabItem.More
                }

                // TopLevelDestination.HOME is the start destination and, therefore, part of any stack
                else -> {
                    selectedItem.value = TabItem.Home
                }
            }
        }
        addOnDestinationChangedListener(listener)

        onDispose {
            removeOnDestinationChangedListener(listener)
        }
    }

    return selectedItem
}

private fun NavHostController.navigateToTabItem(
    item: TabItem,
    restoreTabStack: Boolean
) {
    navigate(item.navGraphRoute) {
        // Pop up to the start destination of the graph to
        // avoid building up a large stack of destinations
        // on the back stack as users select items
        popUpTo(graph.findStartDestination().id) {
            saveState = restoreTabStack
        }
        // Avoid multiple copies of the same destination when
        // reselecting the same item
        launchSingleTop = true
        // Restore state when reselecting a previously selected item
        restoreState = restoreTabStack
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.