我正在使用 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")
}
}
}
}
}
我想要的是以下行为:
-> 我想回家
最后,我在 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
}
}