将 Jetpack Navigation 迁移到 Navigation Compose

借助 Navigation Compose API,您可以在 在 Compose 应用中利用 Jetpack Navigation 组件, 基础架构和功能。

本页介绍了如何从基于 fragment 的 Jetpack Navigation 迁移到 Navigation Compose,是基于 View 的界面向 Jetpack 大规模迁移的一部分 写邮件。

迁移先决条件

当您能够替换掉所有之前使用的 具有相应屏幕可组合项的 fragment。屏幕可组合项可以包含 混合使用 Compose 和 View 内容,但所有导航目的地都必须 可组合项来启用 Navigation Compose 迁移。在此之前,您应该 继续在互操作性 View 中使用基于 Fragment 的 Navigation 组件 Compose 代码库。如需了解详情,请参阅导航互操作性文档 信息。

在纯 Compose 应用中使用 Navigation Compose 不是前提条件。您可以 继续使用 基于 Fragment 的导航组件,前提是您保持 用于托管可组合项内容的 fragment。

迁移步骤

无论您是遵循我们推荐的迁移策略,还是采取 或者使用另一种方法,此时所有导航目的地 screen 可组合项,其中 fragment 仅充当可组合容器。目前 您可以迁移到 Navigation Compose。

如果您的应用已遵循 UDF 设计模式和我们的指南 架构,迁移到 Jetpack Compose 和 Navigation Compose 不应 需要对应用的其他层(界面层除外)进行重大重构。

如需迁移到 Navigation Compose,请按以下步骤操作:

  1. Navigation Compose 依赖项添加到您的应用。
  2. 创建一个 App-level 可组合项,并将其添加到 Activity 中作为 Compose 入口点,替换了 View 布局的设置:

    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. 在 App 可组合项内创建应用的 NavHost,并将 navController:

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

  5. 添加 composable 目的地以构建导航图。如果每个 屏幕之前已迁移到 Compose,此步骤仅包括 将这些屏幕可组合项从 fragment 提取到 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 的方式 每个 screen 可组合项。您通常可以使用 Hilt 注入及其集成 通过 hiltViewModel 使用 Compose 和 Navigation:

    @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 中的类型安全,请阅读 Safe Args 部分

      @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. 移除所有 Fragment、相关的 XML 布局、不必要的导航和其他 过时的 fragment 和 Jetpack Navigation 依赖项。

您可以在 设置文档

常见用例

无论您使用哪个导航组件,导航组件的 导航应用

迁移的常见用例包括:

有关这些用例的更多详细信息,请参见使用 写邮件

Safe Args

与 Jetpack Navigation 不同,Navigation Compose 不支持使用 Safe Args 插件。作为替代方案,您可以通过 Navigation Compose 中,通过设计代码结构以确保其在 运行时。

在导航时检索复杂数据

Navigation Compose 基于字符串路线,与 Jetpack Navigation 不同, 不支持将自定义 Parcelable 和 Serializable 作为参数传递

我们强烈建议您在导航时不要传递复杂的数据对象。 而应传递最少量的必要信息,例如唯一标识符或 ID 的其他形式的 ID,作为执行导航操作时的参数。您应该 将复杂对象作为数据存储在单一可信来源中,例如。有关详情,请参阅 导航

如果您的 fragment 将复杂的对象作为参数传递,请考虑重构 以便从代码中存储和提取这些对象 数据层如需了解更多详情,请参阅 Now in Android 代码库 示例。

限制

本部分介绍 Navigation Compose 的当前限制。

逐步迁移到 Navigation Compose

目前,您无法在使用 Fragment 的同时,使用 Navigation Compose 目标。如需开始使用 Navigation Compose,您的所有 目的地必须是可组合项。您可以在 问题跟踪器

过渡动画

Navigation 2.7.0-alpha01 开始,支持设置自定义 之前从“AnimatedNavHost”改为现在 NavHost 直接支持。仔细阅读版本说明

了解详情

如需详细了解如何迁移到 Navigation Compose,请参阅以下内容 资源: