導覽與返回堆疊

NavController 會保留包含使用者已造訪目的地的「返回堆疊」。當使用者在整個應用程式中瀏覽畫面時,NavController 會在返回堆疊中新增及移除目的地。

做為堆疊,返回堆疊是採用「後進先出」的資料結構。因此 NavController 會將項目推送至堆疊頂端,並從堆疊頂端彈出項目。

基本行為

針對返回堆疊的行為,您應考量以下核心事實:

  • 第一個目的地:使用者開啟應用程式時,NavController 會將第一個目的地推送至返回堆疊頂端。
  • 推送至堆疊:每次呼叫 NavController.navigate(),都會將指定的目的地推送至堆疊頂端。
  • 彈出頂端目的地:輕觸「向上」或「返回」,即可分別呼叫 NavController.navigateUp()NavController.popBackStack() 方法。這些方法會彈出堆疊頂端的目的地。請參閱「導覽原則」頁面,進一步瞭解「向上」和「返回」的差異。

彈回

NavController.popBackStack() 方法會嘗試彈出返回堆疊中目前的目的地,並前往上一個目的地。這樣就能有效將使用者導向導覽記錄中的上一個位置。這個方法會傳回布林值,表示是否已成功彈回該目的地。

彈回特定目的地

您也可以使用 popBackStack() 前往特定目的地,方法是使用其中一個超載。您可以透過多個超載傳入 ID,例如整數 id 或字串 route。這些超載會將使用者導向與指定 ID 相關聯的目的地。重要的是,這些超載會彈出該目的地上方的所有堆疊項目。

這些超載也包含 inclusive 布林值,可決定 NavController 是否應在前往指定目的地後,從返回堆疊中彈出該目的地。

請參考以下簡短的程式碼片段範例:

navController.popBackStack(R.id.destinationId, true)

這裡的 NavController 會彈回目的地,整數 ID 為 destinationId。由於 inclusive 引數的值為 trueNavController 也會彈出返回堆疊中的指定目的地。

處理失敗的彈回

popBackStack() 傳回 false 時,對 NavController.getCurrentDestination() 的後續呼叫會傳回 null。這表示應用程式已彈出返回堆疊中的最後一個目的地。在這種情況下,使用者只會看到空白畫面。

這可能發生於下列情況:

  • popBackStack() 未彈出任何堆疊項目。
  • popBackStack() 已彈出返回堆疊中的目的地,堆疊現為空白。

如要解決這個問題,您必須前往新目的地,或在活動中呼叫 finish() 以結束活動。請參考下列程式碼片段:

Kotlin

...

if (!navController.popBackStack()) {
    // Call finish() on your Activity
    finish()
}

Java

...

if (!navController.popBackStack()) {
    // Call finish() on your Activity
    finish();
}

彈出至目的地

從一個目的地導覽至另一個目的地時,如要移除返回堆疊中的目的地,請在相關聯的 navigate() 函式呼叫中加入 popUpTo() 引數。popUpTo() 會指示 Navigation 程式庫在呼叫 navigate() 時,移除返回堆疊中的某些目的地。參數值是返回堆疊中目的地的 ID。ID 可以是整數 id 或字串 route

您可以在 inclusive 參數中加入值為 true 的引數,指出您在 popUpTo() 中指定的目的地也應從返回堆疊中彈出。

如要以程式輔助方式實作,請在 NavOptions 中將 popUpTo() 傳遞至 navigate(),並將 inclusive 設為 true。這適用於 Compose 和 View。

在彈出時儲存狀態

使用 popUpTo 前往目的地時,您可以視需要儲存所有從返回堆疊中彈出的目的地狀態。

如要啟用這個選項,請在相關聯的 action 或對 NavController.navigate() 的呼叫中,將 popUpToSaveState 定義為 true

您前往目的地時,也可以將 restoreSaveState 定義為 true,即可自動還原 destination 屬性中與目的地相關聯的狀態。

XML 範例

以下是 XML 中使用動作的 popUpTo 範例:

<action
  android:id="@+id/action_a_to_b"
  app:destination="@id/b"
  app:popUpTo="@+id/a"
  app:popUpToInclusive="true"
  app:restoreState=”true”
  app:popUpToSaveState="true"/>

Compose 範例

以下是 Compose 中相同的完整範例:

@Composable
fun MyAppNavHost(
    modifier: Modifier = Modifier,
    navController: NavHostController = rememberNavController(),
    startDestination: String = "destination_a"
) {
    NavHost(
        modifier = modifier,
        navController = navController,
        startDestination = startDestination
    ) {
        composable("destination_a") {
            DestinationA(
                onNavigateToB = {
                // Pop everything up to the "destination_a" destination off the back stack before
                // navigating to the "destination_b" destination
                    navController.navigate("destination_b") {
                        popUpTo("destination_a") {
                            inclusive = true
                            saveState = true
                        }
                    }
                },
            )
        }
        composable("destination_b") { DestinationB(/* ... */) }
    }
}

@ Composable
fun DestinationA(onNavigateToB: () -> Unit) {
    Button(onClick = onNavigateToB) {
        Text("Go to A")
    }
}

如要更精細,可以透過下列方式變更呼叫 NavController.navigate() 的方式:

// Pop everything up to the destination_a destination off the back stack before
// navigating to the "destination_b" destination
navController.navigate("destination_b") {
    popUpTo("destination_a")
}

// Pop everything up to and including the "destination_a" destination off
// the back stack before navigating to the "destination_b" destination
navController.navigate("destination_b") {
    popUpTo("destination_a") { inclusive = true }
}

// Navigate to the "search” destination only if we’re not already on
// the "search" destination, avoiding multiple copies on the top of the
// back stack
navController.navigate("search") {
    launchSingleTop = true
}

如需將選項傳遞至 NavController.navigate() 的一般資訊,請參閱「使用選項導覽」指南。

使用動作彈出

使用操作進行瀏覽時,可以選擇從返回堆疊外關閉其他目的地。舉例來說,如果您的應用程式有起始的登入流程,當使用者登入時,請將所有與登入相關的目的地都從返回堆疊中彈出,讓「返回」按鈕不會將使用者導回登入流程。

延伸閱讀

如需詳細資訊,請參閱以下頁面:

  • 循環導覽:瞭解如何在循環導覽流程中,避免過度填充返回堆疊。
  • 對話方塊目的地:瞭解對話方塊目的地的獨特考量,可如何協助您管理返回堆疊。