Проблема с обратной навигацией Jetpack Compose ⇐ Android
Проблема с обратной навигацией Jetpack Compose
В настоящее время я реализую навигацию по отдельным действиям для функции адаптации моего приложения. Я использую однонаправленный поток данных (UDF), как рекомендует Google, и одну ViewModel для управления состоянием. Каждый тип состояния связан с определенной страницей слайда в моей адаптации.
Вот главный экран:
@Composable веселый OnboardingScreen( модификатор: Модификатор = Модификатор, viewModel: OnboardingViewModel = viewModel(), ) { val navController: NavHostController = RememberNavController() NavHost(navController = navController, startDestination = Onboarding1.route) { составной (Onboarding1.route) { Onboarding1Page(модификатор) { viewModel.doAction( OnboardingAction.NextClickAction( происхождение = Регистрация1 ) ) } } составной (Onboarding2.route) { Регистрация2Page( модификатор, навигацияBack = { navController.navBackWith { //Функция расширения определена дополнительно viewModel.doAction(OnboardingAction.NavBackToAction(it)) } }, навигацияВперед = { viewModel.doAction( OnboardingAction.NextClickAction( происхождение = Регистрация2 ) ) } ) } составной (Onboarding3.route) { //... то же, что Onboarding2, но другой маршрут } компонуемый (OnboardingPush.route) { //... то же, что Onboarding2, но другой маршрут } } состояние val с помощью viewModel.viewState.collectAsState() когда (состояние) { OnboardingState.Onboarding1State -> { navController.navigate(Onboarding1.route) } OnboardingState.Onboarding2State -> { navController.navigate(Onboarding2.route) } OnboardingState.Onboarding3State -> { navController.navigate(Onboarding3.route) } OnboardingState.OnboardingPushState -> { navController.navigate(OnboardingPush.route) } } } Нечетный navController.navBackWith{... — это функция расширения, которую я пишу для ручного управления обратным стеком. Мне нужно это, чтобы быть уверенным, что когда пользователь нажмет кнопку «Назад», он не просто перейдет к предыдущему экрану, но также изменит состояние. Таким образом, мой пользовательский интерфейс будет соответствовать состоянию. Я не хочу автоматически popBackStack, а хочу пройти через ViewModel.
[*]
Пользователь возвращается со страницы 3 -> Модель просмотра получает его
[*]
ViewModel обновляет состояние до Onboarding2State -> пользовательский интерфейс получает его и возвращается обратно
Чтобы обойти эту проблему, я использую BackHandler на каждой странице следующим образом:
@Composable весело Onboarding2Page( модификатор: Модификатор = Модификатор, NavigationBack: () -> Единица измерения, навигацияВперед: () -> Единица измерения ) { BackHandler(onBack = NavigationBack) //...код } Поэтому, когда пользователь ответит на нажатие (или использует жест), это инициирует мой вызов ViewModel:
navController.navBackWith { //Функция расширения определена дополнительно viewModel.doAction(OnboardingAction.NavBackToAction(it)) } Я пробовал несколько способов реализации .navBackWith со смешанным результатом.
Когда я возвращаюсь назад в первый раз, со страницы 4 (OnboardingPush), я удаляю записи n-1 и n-2 из стека, а затем снова перехожу вперед к n-2. На данный момент в нем должно быть только 3 записи. Кажется, все в порядке, но когда я снова возвращаюсь назад, по какой-то волшебной причине я получаю 4 записи в стек. По какой-то причине мой NavGraph был восстановлен (?!)
Вот текущая реализация NavHostController.navBackWith, не обращайте внимания на простоту кода, после десятков итераций я просто хочу найти работающее решение.
fun NavHostController.navBackWith(action: (String) -> Unit) { если (this.graph.nodes.size() == 1) { это.popBackStack() } else if (this.graph.nodes.size() >= 2) { val LastRoute = this.currentBackStackEntry?.destination?.route val prevRoute = this.previousBackStackEntry?.destination?.route if (lastRoute == null || prevRoute == null) { это.popBackStack() } еще { val узлы = this.graph.nodes вар LastKey: Int = 0 вар prevKey: Int = 0 nodes.forEach {ключ, значение -> if (value.route == LastRoute) LastKey = ключ if (value.route == prevRoute) prevKey = ключ } узлы.remove(lastKey) nodes.remove(prevKey) action.invoke(prevRoute) } } } Я не могу использовать какие-либо внешние библиотеки для навигации и не хочу иметь еще один собственный стек. В идеале я бы повторно использовал основной.
В настоящее время я реализую навигацию по отдельным действиям для функции адаптации моего приложения. Я использую однонаправленный поток данных (UDF), как рекомендует Google, и одну ViewModel для управления состоянием. Каждый тип состояния связан с определенной страницей слайда в моей адаптации.
Вот главный экран:
@Composable веселый OnboardingScreen( модификатор: Модификатор = Модификатор, viewModel: OnboardingViewModel = viewModel(), ) { val navController: NavHostController = RememberNavController() NavHost(navController = navController, startDestination = Onboarding1.route) { составной (Onboarding1.route) { Onboarding1Page(модификатор) { viewModel.doAction( OnboardingAction.NextClickAction( происхождение = Регистрация1 ) ) } } составной (Onboarding2.route) { Регистрация2Page( модификатор, навигацияBack = { navController.navBackWith { //Функция расширения определена дополнительно viewModel.doAction(OnboardingAction.NavBackToAction(it)) } }, навигацияВперед = { viewModel.doAction( OnboardingAction.NextClickAction( происхождение = Регистрация2 ) ) } ) } составной (Onboarding3.route) { //... то же, что Onboarding2, но другой маршрут } компонуемый (OnboardingPush.route) { //... то же, что Onboarding2, но другой маршрут } } состояние val с помощью viewModel.viewState.collectAsState() когда (состояние) { OnboardingState.Onboarding1State -> { navController.navigate(Onboarding1.route) } OnboardingState.Onboarding2State -> { navController.navigate(Onboarding2.route) } OnboardingState.Onboarding3State -> { navController.navigate(Onboarding3.route) } OnboardingState.OnboardingPushState -> { navController.navigate(OnboardingPush.route) } } } Нечетный navController.navBackWith{... — это функция расширения, которую я пишу для ручного управления обратным стеком. Мне нужно это, чтобы быть уверенным, что когда пользователь нажмет кнопку «Назад», он не просто перейдет к предыдущему экрану, но также изменит состояние. Таким образом, мой пользовательский интерфейс будет соответствовать состоянию. Я не хочу автоматически popBackStack, а хочу пройти через ViewModel.
[*]
Пользователь возвращается со страницы 3 -> Модель просмотра получает его
[*]
ViewModel обновляет состояние до Onboarding2State -> пользовательский интерфейс получает его и возвращается обратно
Чтобы обойти эту проблему, я использую BackHandler на каждой странице следующим образом:
@Composable весело Onboarding2Page( модификатор: Модификатор = Модификатор, NavigationBack: () -> Единица измерения, навигацияВперед: () -> Единица измерения ) { BackHandler(onBack = NavigationBack) //...код } Поэтому, когда пользователь ответит на нажатие (или использует жест), это инициирует мой вызов ViewModel:
navController.navBackWith { //Функция расширения определена дополнительно viewModel.doAction(OnboardingAction.NavBackToAction(it)) } Я пробовал несколько способов реализации .navBackWith со смешанным результатом.
Когда я возвращаюсь назад в первый раз, со страницы 4 (OnboardingPush), я удаляю записи n-1 и n-2 из стека, а затем снова перехожу вперед к n-2. На данный момент в нем должно быть только 3 записи. Кажется, все в порядке, но когда я снова возвращаюсь назад, по какой-то волшебной причине я получаю 4 записи в стек. По какой-то причине мой NavGraph был восстановлен (?!)
Вот текущая реализация NavHostController.navBackWith, не обращайте внимания на простоту кода, после десятков итераций я просто хочу найти работающее решение.
fun NavHostController.navBackWith(action: (String) -> Unit) { если (this.graph.nodes.size() == 1) { это.popBackStack() } else if (this.graph.nodes.size() >= 2) { val LastRoute = this.currentBackStackEntry?.destination?.route val prevRoute = this.previousBackStackEntry?.destination?.route if (lastRoute == null || prevRoute == null) { это.popBackStack() } еще { val узлы = this.graph.nodes вар LastKey: Int = 0 вар prevKey: Int = 0 nodes.forEach {ключ, значение -> if (value.route == LastRoute) LastKey = ключ if (value.route == prevRoute) prevKey = ключ } узлы.remove(lastKey) nodes.remove(prevKey) action.invoke(prevRoute) } } } Я не могу использовать какие-либо внешние библиотеки для навигации и не хочу иметь еще один собственный стек. В идеале я бы повторно использовал основной.
-
- Похожие темы
- Ответы
- Просмотры
- Последнее сообщение