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

The Navigation Compose API allows you to navigate between composables in a Compose app, while taking advantage of the Jetpack Navigation's component, infrastructure, and features.

This page describes how to migrate from a Fragment-based Jetpack Navigation to Navigation Compose, as part of the larger, View-based UI migration to Jetpack Compose.

Migration prerequisites

You can migrate to Navigation Compose once you're able to replace all of your Fragments with corresponding screen composables . Screen composables can contain a mix of Compose and View content , but all navigation destinations must be composables to enable Navigation Compose migration. До тех пор вам следует продолжать использовать компонент навигации на основе фрагментов в базе кода взаимодействия View и Compose. Дополнительные сведения см. в документации по взаимодействию навигации .

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

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

Whether you are following our recommended migration strategy or taking another approach, you'll reach a point where all navigation destinations are screen composables, with Fragments acting only as composable containers. На этом этапе вы можете перейти на Navigation Compose.

If your app is already following a UDF design pattern and our guide to architecture , migrating to Jetpack Compose and Navigation Compose shouldn't require major refactors of other layers of your app, apart from the UI layer.

Чтобы перейти на 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. Set up the NavController in a place where all composables that need to reference it have access to it (this is usually inside your App composable). This approach follows the principles of state hoisting and allows you to use the NavController as the source of truth for navigating between composable screens and maintaining the back stack:

    @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, можно найти в документации по установке .

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

No matter which Navigation component you're using, the same principles of navigation apply .

Common use cases when migrating include the following:

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

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

В отличие от Jetpack Navigation, Navigation Compose не поддерживает использование плагина Safe Args для генерации кода. Instead, you can achieve type safety with Navigation Compose through structuring your code to make it type-safe at runtime.

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

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

We strongly recommend not passing around complex data objects when navigating. Вместо этого передайте минимально необходимую информацию, например уникальный идентификатор или другую форму идентификатора, в качестве аргументов при выполнении действий навигации. Сложные объекты следует хранить в виде данных в одном источнике достоверности, например на уровне данных . Дополнительную информацию см. в разделе Получение сложных данных при навигации .

Если ваши фрагменты передают сложные объекты в качестве аргументов, сначала рассмотрите возможность рефакторинга вашего кода таким образом, чтобы можно было хранить и извлекать эти объекты из уровня данных. Примеры см. в репозитории 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 с помощью практической лаборатории разработки кода.
  • Now in Android repository : A fully functional Android app built entirely with Kotlin and Jetpack Compose, which follows Android design and development best practices and includes Navigation Compose.
  • Миграция Sunflower в Jetpack Compose : запись в блоге, в которой документируется путь миграции примера приложения Sunflower из Views в Compose, который также включает миграцию в Navigation Compose.
  • Jetnews для каждого экрана : сообщение в блоге, в котором документируется рефакторинг и миграция примера Jetnews для поддержки всех экранов с помощью Jetpack Compose и Navigation Compose.
{% дословно %} {% дословно %} {% дословно %} {% дословно %}