Navigation Component и multi backstack navigation

от автора

Вы сейчас в четвертой части большого материала про Navigation Component в многомодульном проекте. Если вы уже знаете:

То добро пожаловать в заключительную часть истории о моем опыте с этой прекрасной библиотекой — про решение для iOS-like multistack-навигации.

Если не знаете, то выйдите и зайдите нормально прочитайте сначала три статьи выше.

В дополнение к библиотеке Navigation Component Google выпустили несколько интерфейсных дополнений под названием NavigationUI, которые помогут вам подключить навигацию к BottomBar, Menu и прочим стандартным компонентам. Но часто поступают требования, чтобы на каждой вкладке был свой стек и текущие состояния сохранялись при переходе между ними. К сожалению, из коробки Navigation Component и NavigationUI так не умеют.

Поддержку такого подхода представили сами Google в своем architecture-components-samples репозитории на GitHub (https://github.com/android/architecture-components-samples/tree/master/NavigationAdvancedSample). Суть его проста:

  1. Добавляем FragmentContainer.

  2. Создаем NavHostFragment и граф под каждую вкладку.

  3. При выборе вкладки присоединяем необходимый NavHostFragment и отсоединяем текущий с помощью транзакций FragmentManager-a.

Но в ходе работы с этим решением я переделал некоторые моменты, связанные со спецификой проекта:

  • Многие приложения имеют sign in / up flow, on boarding и прочие экраны, которые не должны входить в стеки, но даже в таком случае все достаточно просто оборачивается стандартными средствами. Навигацию между этими частями можно выстроить уже как обычно, например, как в предыдущей части.

  • В примере все стеки инициализируются сразу при старте приложения. Связано это с корректной работой NavigationBottomBar и обработкой Deep Link-ов. Но я часто сталкивался с проектами, где deep link-и не нужны и бар навигации требует кастомизации. Проект, на котором я обкатывал подход — не исключение. Глядя на оригинальный файл NavigationExtensions в 250 loc, я решил выбросить все ненужное и сделать lazy-инициализацию NavHost-ов, оставив только основные функции:

Функция поиска / инициализации требуемого NavHost-фрагмента:

fun obtainNavHostFragment(         fragmentManager: FragmentManager,         fragmentTag: String,         navGraphId: Int,         containerId: Int ): NavHostFragment {         // If the Nav Host fragment exists, return it         val existingFragment =         fragmentManager.findFragmentByTag(fragmentTag) as NavHostFragment?         existingFragment?.let { return it }         // Otherwise, create it and return it.         val navHostFragment = NavHostFragment.create(navGraphId)         fragmentManager.beginTransaction()                 .add(containerId, navHostFragment, fragmentTag)                 .commitNow()         return navHostFragment }

Функция смены NavHost-ов:

protected fun selectTab(tab: Tab) {         val newFragment = obtainNavHostFragment(                 childFragmentManager,                 getFragmentTag(tabs.indexOf(tab)),                 tab.graphId,                 containerId          )          val fTrans = childFragmentManager.beginTransaction()          with(fTrans) {                   if (selectedFragment != null) detach(selectedFragment!!)                   attach(newFragment)                  commitNow()          }          selectedFragment = newFragment          currentNavController = selectedFragment!!.navController          tabSelected(tab) }

Функция своей обработки нажатия на кнопку “Back”:

activity?.onBackPressedDispatcher?.addCallback(          viewLifecycleOwner,          object: OnBackPressedCallback(true){                   override fun handleOnBackPressed() {                            val isNavigatedUp = currentNavController.navigateUp()                            if(isNavigatedUp){                                     return                            }else{                                     activity?.finish()                            }                   }          } )

В итоге

Таким образом мы получаем iOS-like навигацию и даже лучше, так как имеем lazy-нагрузку на стеках и меньшее количество кода. Приятный бонус — мы имеем полностью очевидную и прозрачную схему навигации, которую просто масштабировать и модифицировать.

ссылка на оригинал статьи https://habr.com/ru/post/539684/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *