У меня проблема с иерархией моей навигации по созданию сообщений. Версия Compose-Navigation, которую я использую: androidx.navigation:navigation-compose:2.7.7
.
Я пытаюсь реализовать простую навигацию с нижней навигацией и тремя вкладками. На каждой вкладке имеется собственная вложенная навигация, состоящая из двух экранов: основного содержимого вкладки и экрана сведений, который повторно используется во вложенных графиках.
Основное действие с NavHost для вкладок:
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContent {
TestNavigationTheme {
val tabsController = rememberNavController()
Scaffold(
bottomBar = {
NavigationBar {
val navBackStackEntry by tabsController.currentBackStackEntryAsState()
val currentDestination = navBackStackEntry?.destination
BottomTabs.items.forEach { navigationItem ->
NavigationBarItem(
label = { Text(navigationItem.title) },
selected = currentDestination
?.hierarchy
?.any { it.route == navigationItem.route } == true,
icon = { Icon(navigationItem.icon, navigationItem.title) },
onClick = {
tabsController.navigate(navigationItem.route) {
popUpTo(tabsController.graph.findStartDestination().id) {
saveState = true
}
launchSingleTop = true
restoreState = true
}
}
)
}
}
}
) { paddingValues ->
NavHost(
navController = tabsController,
startDestination = BottomTabs.Feed.route,
modifier = Modifier.padding(paddingValues = paddingValues)
) {
navigation(
route = BottomTabs.Feed.route,
startDestination = "feed"
) {
composable(route = "feed") {
FeedScreen {
tabsController.navigate("details/feed")
}
}
details()
}
navigation(
route = BottomTabs.Search.route,
startDestination = "search"
) {
composable(route = "search") {
SearchScreen {
tabsController.navigate("details/search")
}
}
details()
}
navigation(
route = BottomTabs.Favourites.route,
startDestination = "fav"
) {
composable(route = "fav") {
FavouritesScreen {
tabsController.navigate("details/fav")
}
}
details()
}
}
}
}
}
}
private fun NavGraphBuilder.details() {
composable(
route = "details/{argument}",
arguments = listOf(
navArgument("argument") { type = NavType.StringType }
)
) { backStackEntry ->
DetailsScreen(
argument = backStackEntry
.arguments
?.getString("argument")
?: "Couldn't obtain the argument"
)
}
}
}
Нижние вкладки:
sealed class BottomTabs(val route: String, val title: String, val icon: ImageVector) {
data object Feed : BottomTabs("feed-tab", "Feed", Icons.Default.Home)
data object Search : BottomTabs("search-tab", "Search", Icons.Default.Search)
data object Favourites : BottomTabs("fav-tab", "Favourites", Icons.Default.Favorite)
companion object {
val items = listOf(Feed, Search, Favourites)
}
}
Экраны вкладок:
@Composable
fun FeedScreen(navigateToDetails: () -> Unit) {
Column(
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier.fillMaxSize()
) {
Text("Hi, I'm Feed screen")
Spacer(modifier = Modifier.size(40.dp))
Button(onClick = navigateToDetails) {
Text(text = "Go to details")
}
}
}
@Composable
fun SearchScreen(navigateToDetails: () -> Unit) {
Column(
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier.fillMaxSize()
) {
Text("Hi, I'm Search screen")
Spacer(modifier = Modifier.size(40.dp))
Button(onClick = navigateToDetails) {
Text(text = "Go to details")
}
}
}
@Composable
fun FavouritesScreen(navigateToDetails: () -> Unit) {
Column(
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier.fillMaxSize()
) {
Text("Hi, I'm Favourites screen")
Spacer(modifier = Modifier.size(40.dp))
Button(onClick = navigateToDetails) {
Text(text = "Go to details")
}
}
}
Экран подробностей:
@Composable
fun DetailsScreen(argument: String) {
Column(
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier.fillMaxSize()
) {
Text("I'm details screen")
Text("Your argument is: $argument")
}
}
Вы можете найти этот код внутри NavigationBarItem, который я взял из Руководства Google по навигации:
selected = currentDestination
?.hierarchy
?.any { it.route == navigationItem.route } == true
Когда я переключаюсь на третью вкладку («fav-tab») и нажимаю кнопку, чтобы открыть экран подробностей, экран подробностей открывается правильно на вкладке fav. Однако на панели навигации выделена выбранная вкладка ленты вместо вкладки избранного.
Я попытался отладить цикл рекомпозиции навигации, поместив точку останова в NavigationBarItem. Вот иерархия:
Я не понимаю, почему feed-tab
оказывается корнем иерархии. На мой взгляд, должно быть:
"details/{argument}"
"fav-tab"
потому что я перешел на экран сведений с fav-tab
, а не с feed-tab
.
Что я делаю не так в своей реализации? Любые мысли и советы будут очень признательны!
@ianhanniballake да, он ведет себя так же
Причиной проблемы был маршрут экрана сведений. Это было одинаково для всех трех навигационных графиков.
Согласно документации, «Route однозначно идентифицирует пункт назначения и все необходимые для него данные».
Итак, я добавил префиксы в составные детали(), и теперь все работает нормально.
Видите ли вы ту же проблему при обновлении до версии Navigation 2.8.0-beta04?