Jetpack Compose Navigation: PopUpTo Screen из экранов, которые имеют тот же маршрут, кроме аргумента
навигация составить версию
2.4.0-alpha06
У меня есть панель навигации, и часть элементов динамически генерируется ViewModel.
Примеры элементов:
- Дом
- А
- B
- С ...
- Настройки
где A, B, C, ... все имеют одинаковые
Screen
называется
Category
, только с другими переданными аргументами (например, Категория / A, Категория / 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 пытается только найти точное совпадение из backstack, поэтому ничего не выскакивает.
Есть ли реальный способ компоновки-навигации для решения этой проблемы? или я должен сохранить последний "заголовок" где-нибудь во ViewModel и использовать его?
Этот учебник от Google имеет аналогичную структуру, но он просто складывает экраны, поэтому, возвращаясь с экрана A -> B -> A и щелкая назад, вы просто вернетесь к B -> A, что не является идеальным поведением для меня.
Заранее спасибо.
3 ответа
Когда вы указываете
popUpTo
в этом случае вы должны передать тот же элемент, к которому вы переходите:
navController.navigate(item.route) {
popUpTo(item.route) {
inclusive = true
}
}
Также не уверен, нужно ли вам указывать
saveState
в этом случае решать вам:
Следует ли сохранять задний стек и состояние всех пунктов назначения между текущим пунктом назначения и идентификатором для последующего восстановления с помощью
NavOptionsBuilder.restoreState
или атрибут restoreState, используя тот жеNavOptionsBuilder.popUpTo
ID (примечание: этот совпадающий ID верен независимо от того, истинно ли включено или ложно).
Вы можете сделать функцию расширения, чтобы обслуживать функциональность 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)
Вдохновленный @Philip Духова в ответ , я смог достичь того, чего я хотел.
...
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
, Скрин с другим аргументом почему-то не перекомпонован, но это наверное уже другая тема.