將 Jetpack Navigation 遷移至 Navigation Compose

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

本頁面說明如何從以 Fragment 為基礎的 Jetpack Navigation 遷移至 Navigation Compose,為 Jetpack Compose 的大型 UI 遷移作業。

遷移作業必備條件

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

不一定要在僅限 Compose 的應用程式中使用 Navigation Compose。只要保留 Fragment 以便代管可組合內容,您可以繼續使用以片段為基礎的導覽元件

遷移步驟

無論您是依循我們建議的遷移策略,還是採取其他做法,您都會發現所有導覽目的地都是畫面可組合項,而 Fragment 僅做為可組合容器運作。在這個階段,您可以遷移至 Navigation Compose。

如果您的應用程式已在遵循 UDF 設計模式架構指南,遷移至 Jetpack Compose 和 Navigation Compose 就不需要為應用程式的其他層 (除了 UI 層) 進行重大重構。

如要遷移至 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 的相關資訊。

常見用途

無論您使用哪個導覽元件,都適用相同的導覽原則

遷移的常見用途包括:

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

Safe Args

與 Jetpack Navigation 不同,Navigation Compose 不支援使用 Safe Args 外掛程式產生程式碼。您可以改為使用 Navigation Compose 實現類型安全,透過建構程式碼在執行階段確保類型安全。

瀏覽時擷取複雜資料

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

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

如果您的 Fragment 會將複雜物件做為引數傳遞,建議您先重構程式碼,以允許儲存及擷取資料層中的這些物件。如需範例,請參閱 Now in Android 存放區

限制

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

逐步遷移至 Navigation Compose

目前,若在程式碼中仍使用 Fragment 做為目的地,就無法使用 Navigation Compose。如要開始使用 Navigation Compose,所有目的地都必須是可組合項。如要追蹤這項功能要求,請造訪 Issue Tracker

轉場動畫

Navigation 2.7.0-alpha01 起,NavHost 現已直接支援設定自訂轉場效果 (原本從 AnimatedNavHost)。詳閱版本資訊,瞭解更多資訊。

瞭解詳情

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