Перенос навигации Jetpack в Navigation Compose

API Navigation Compose позволяет перемещаться между составными объектами в приложении Compose, используя при этом преимущества компонента, инфраструктуры и функций Jetpack Navigation .

На этой странице описывается, как перейти от навигации Jetpack на основе фрагментов к Navigation Compose в рамках более крупной миграции пользовательского интерфейса на основе представлений в Jetpack Compose.

Предварительные условия миграции

Вы можете перейти на Navigation Compose, как только сможете заменить все свои фрагменты соответствующими компонентами экрана . Компонуемые элементы экрана могут содержать смесь содержимого Compose и View , но все пункты назначения навигации должны быть составными, чтобы обеспечить миграцию Navigation Compose. До тех пор вам следует продолжать использовать компонент навигации на основе фрагментов в базе кода взаимодействия View и Compose. Дополнительную информацию см. в документации по взаимодействию навигации .

Использование Navigation Compose в приложении, предназначенном только для создания сообщений, не является обязательным условием. Вы можете продолжать использовать компонент навигации на основе фрагментов , пока сохраняете фрагменты для размещения компонуемого контента .

Этапы миграции

Независимо от того, следуете ли вы нашей рекомендованной стратегии миграции или используете другой подход, вы достигнете точки, когда все пункты назначения навигации будут компонуемыми на экране, а фрагменты будут действовать только как компонуемые контейнеры. На этом этапе вы можете перейти на Navigation Compose.

Если ваше приложение уже соответствует шаблону проектирования UDF и нашему руководству по архитектуре , переход на Jetpack Compose и Navigation Compose не должен требовать серьезных рефакторингов других уровней вашего приложения, кроме уровня пользовательского интерфейса.

Чтобы перейти на Navigation Compose, выполните следующие действия:

  1. Добавьте зависимость Navigation Compose в свое приложение.
  2. Создайте компонуемый объект App-level и добавьте его в свою Activity в качестве точки входа для создания, заменив настройку макета представления:

    class SampleActivity : ComponentActivity() {
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            // setContentView<ActivitySampleBinding>(this, R.layout.activity_sample)
            setContent {
                SampleApp(/* ... */)
            }
        }
    }

  3. Настройте NavController в месте, где все компонуемые элементы, которым необходимо ссылаться на него, имеют к нему доступ (обычно это находится внутри компонуемого App ). Этот подход следует принципам поднятия состояния и позволяет использовать NavController в качестве источника данных для навигации между компонуемыми экранами и поддержки обратного стека:

    @Composable
    fun SampleApp() {
        val navController = rememberNavController()
        // ...
    }

  4. Создайте NavHost вашего приложения внутри составного приложения и передайте navController :

    @Composable
    fun SampleApp() {
        val navController = rememberNavController()
    
        SampleNavHost(navController = navController)
    }
    
    @Composable
    fun SampleNavHost(
        navController: NavHostController
    ) {
        NavHost(navController = navController, startDestination = "first") {
            // ...
        }
    }

  5. Добавьте composable пункты назначения, чтобы построить навигационный граф. Если каждый экран ранее был перенесен в Compose, этот шаг состоит только из извлечения этих компонуемых экранов из ваших фрагментов в composable места назначения:

    class FirstFragment : Fragment() {
    
        override fun onCreateView(
            inflater: LayoutInflater,
            container: ViewGroup?,
            savedInstanceState: Bundle?
        ): View {
            return ComposeView(requireContext()).apply {
                setContent {
                    // FirstScreen(...) EXTRACT FROM HERE
                }
            }
        }
    }
    
    @Composable
    fun SampleNavHost(
        navController: NavHostController
    ) {
        NavHost(navController = navController, startDestination = "first") {
            composable("first") {
                FirstScreen(/* ... */) // EXTRACT TO HERE
            }
            composable("second") {
                SecondScreen(/* ... */)
            }
            // ...
        }
    }

  6. Если вы следовали рекомендациям по проектированию пользовательского интерфейса Compose , в частности, как ViewModel и события навигации должны передаваться в составные элементы, следующим шагом будет изменение способа предоставления ViewModel для каждого компонуемого экрана. Вы часто можете использовать Hilt-инъекцию и ее точку интеграции с Compose и Navigation через hiltViewModel :

    @Composable
    fun FirstScreen(
        // viewModel: FirstViewModel = viewModel(),
        viewModel: FirstViewModel = hiltViewModel(),
        onButtonClick: () -> Unit = {},
    ) {
        // ...
    }

  7. Замените все навигационные вызовы findNavController() вызовами navController и передайте их как события навигации на каждый составной экран, а не передавайте весь navController . Этот подход соответствует лучшим практикам предоставления событий из составных функций вызывающим сторонам и сохраняет navController в качестве единственного источника истины.

    1. Если вы ранее использовали плагин Safe Args для создания направлений и действий навигации, замените его маршрутом — строковым путем к вашему составному элементу, который уникален для каждого пункта назначения.
    2. Сведения о замене Safe Args при передаче данных см. в разделе «Навигация с аргументами» .
    3. Чтобы узнать о безопасности типов в Navigation Compose, прочтите раздел «Безопасные аргументы» ниже.

      @Composable
      fun SampleNavHost(
          navController: NavHostController
      ) {
          NavHost(navController = navController, startDestination = "first") {
              composable("first") {
                  FirstScreen(
                      onButtonClick = {
                          // findNavController().navigate(firstScreenToSecondScreenAction)
                          navController.navigate("second_screen_route")
                      }
                  )
              }
              composable("second") {
                  SecondScreen(
                      onIconClick = {
                          // findNavController().navigate(secondScreenToThirdScreenAction)
                          navController.navigate("third_screen_route")
                      }
                  )
              }
              // ...
          }
      }

  8. Удалите все фрагменты, соответствующие макеты XML, ненужную навигацию и другие ресурсы, а также устаревшие зависимости фрагментов и навигации Jetpack.

Те же действия, а также дополнительные сведения, связанные с Navigation Compose, можно найти в документации по установке .

Распространенные случаи использования

Независимо от того, какой компонент навигации вы используете, применяются одни и те же принципы навигации .

Общие случаи использования при миграции включают следующее:

Более подробную информацию об этих вариантах использования см. в разделе «Навигация с помощью Compose» .

Безопасные аргументы

В отличие от Jetpack Navigation, Navigation Compose не поддерживает использование плагина Safe Args для генерации кода. Вместо этого вы можете обеспечить безопасность типов с помощью Navigation Compose , структурировав свой код так, чтобы сделать его типобезопасным во время выполнения.

Получение сложных данных при навигации

Navigation Compose основан на строковом маршруте и, в отличие от Jetpack Navigation, не поддерживает передачу пользовательских Parcelables и Serializables в качестве аргументов .

Мы настоятельно рекомендуем не обходить сложные объекты данных при навигации. Вместо этого передайте минимально необходимую информацию, например уникальный идентификатор или другую форму идентификатора, в качестве аргументов при выполнении действий навигации. Сложные объекты следует хранить в виде данных в одном источнике достоверности, например на уровне данных . Дополнительную информацию см. в разделе Получение сложных данных при навигации .

Если ваши фрагменты передают сложные объекты в качестве аргументов, сначала рассмотрите возможность рефакторинга вашего кода таким образом, чтобы можно было хранить и извлекать эти объекты из уровня данных. Примеры см. в репозитории Now in Android .

Ограничения

В этом разделе описаны текущие ограничения для Navigation Compose.

Инкрементальный переход на Navigation Compose

В настоящее время вы не можете использовать Navigation Compose, продолжая использовать фрагменты в качестве пунктов назначения в своем коде. Чтобы начать использовать Navigation Compose, все ваши пункты назначения должны быть составными. Вы можете отслеживать этот запрос на функцию в Issue Tracker .

Анимация перехода

Начиная с Navigation 2.7.0-alpha01 , поддержка настройки пользовательских переходов, ранее существовавшая в AnimatedNavHost , теперь напрямую поддерживается в NavHost . Прочтите примечания к выпуску для получения дополнительной информации.

Узнать больше

Дополнительные сведения о переходе на Navigation Compose см. в следующих ресурсах:

  • Лаборатория разработки Navigation Compose : изучите основы Navigation Compose с помощью практической лаборатории разработки кода.
  • Теперь в репозитории Android : полнофункциональное приложение для Android, полностью созданное с помощью Kotlin и Jetpack Compose, которое следует передовым практикам проектирования и разработки Android и включает в себя Navigation Compose.
  • Миграция Sunflower в Jetpack Compose : запись в блоге, в которой документируется путь миграции примера приложения Sunflower из Views в Compose, который также включает миграцию в Navigation Compose.
  • Jetnews для каждого экрана : сообщение в блоге, в котором документируется рефакторинг и миграция примера Jetnews для поддержки всех экранов с помощью Jetpack Compose и Navigation Compose.
{% дословно %} {% дословно %} {% дословно %} {% дословно %}