將 Jetpack Navigation 遷移至 Navigation Compose

Navigation Compose API 可讓您在 Compose 應用程式中瀏覽可組合項,同時利用 Jetpack Navigation 的元件、基礎架構和功能。

本頁面說明如何在以 View 為基礎的大型 UI 遷移至 Jetpack Compose 的過程中,從以 Fragment 為基礎的 Jetpack Navigation 遷移至 Navigation Compose。

遷移作業必備條件

當您能將所有片段替換為對應的畫面可組合項後,即可遷移至 Navigation Compose。螢幕可組合項可以包含混合的 Compose 和 View 內容,但所有導覽目的地都必須是可組合項,才能啟用 Navigation Compose 遷移作業。在那之前,請在互通性檢視畫面和 Compose 程式碼集中繼續使用以片段為基礎的 Navigation 元件。詳情請參閱導覽互通性說明文件

即使在僅支援 Compose 的應用程式中使用 Navigation Compose 也不是必要條件。只要保留用於代管可組合項內容的片段,您就可以繼續使用 以片段為基礎的 Navigation 元件

遷移步驟

無論您是採用我們建議的遷移策略還是採取其他做法,都會達到所有導覽目的地都是螢幕可組合項的時間點,而 Fragment 僅做為可組合容器。在這個階段,您可以遷移至 Navigation Compose。

如果您的應用程式已遵循 UDF 設計模式架構指南,除了 UI 層外,遷移至 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. 在應用程式可組合項中建立應用程式的 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 UI 的指南,特別是 ViewModel 和導覽事件應如何傳遞至可組合項,則下一步是變更為各個螢幕可組合項提供 ViewModel 的方式。您可以透過 hiltViewModel,將 Hilt 插入及其整合點與 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 依附元件。

您可以在設定說明文件中找到相同步驟,瞭解更多 Navigation Compose 相關的詳細資料。

常見用途

無論您使用何種 Navigation 元件,都適用相同的導覽原則

遷移作業的常見用途包括:

如要進一步瞭解這些用途,請參閱「使用 Compose 進行導覽」。

Safe Args

與 Jetpack Navigation 不同,Navigation Compose 不支援使用 Safe Args 外掛程式產生程式碼。但可以改為使用 Navigation Compose 達成類型安全,方法是建構程式碼,在執行階段確保類型安全。

瀏覽時擷取複雜資料

Navigation Compose 是以字串路徑為基礎,與 Jetpack Navigation 不同,不支援將自訂 Parcelable 和 Serializable 做為引數傳遞

強烈建議您不要在導覽時傳遞複雜的資料物件。請在執行導覽動作時,將最低必要資訊 (例如專屬 ID 或其他形式的 ID) 做為引數傳遞。建議您將複雜的物件儲存為單一可靠來源 (例如資料層) 的資料。詳情請參閱「在導覽時擷取複雜資料」。

如果片段是以引數形式傳遞複雜物件,請考慮先重構程式碼,以便從資料層儲存及擷取這些物件。如需範例,請參閱 Now in Android 存放區

限制

本節說明 Navigation Compose 目前的限制。

逐步遷移至 Navigation Compose

目前,您無法在程式碼中使用 Fragment 做為目的地時,無法使用 Navigation Compose。如要開始使用 Navigation Compose,所有的目的地都必須是可組合項。您可以透過 Issue Tracker 回報這項功能要求

轉場動畫

Navigation 2.7.0-alpha01 起,NavHost 直接支援設定自訂轉場效果 (原先來自 AnimatedNavHost)。詳情請參閱版本資訊

瞭解詳情

如要進一步瞭解如何遷移至 Navigation Compose,請參閱下列資源:

  • Navigation Compose 程式碼研究室:透過實作程式碼研究室瞭解 Navigation Compose 的基本概念。
  • Now in Android 存放區:完全使用 Kotlin 和 Jetpack Compose 建構而成、功能齊全的 Android 應用程式,遵循 Android 設計與開發最佳做法,並且包含 Navigation Compose。
  • 將 Sunflower 遷移至 Jetpack Compose:這篇網誌文章說明將 Sunflower 範例應用程式從 View 遷移至 Compose 的流程,同時包括遷移至 Navigation Compose。
  • 每個畫面的 Jetnews:網誌文章介紹 Jetnews 範例的重構及遷移,藉此透過 Jetpack Compose 和 Navigation Compose 支援所有螢幕。