導覽與返回堆疊

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 前往目的地時,您可以選擇儲存 返回堆疊和從返回堆疊中彈出的所有目的地狀態。你可以 導覽至該目的地時,還原返回堆疊和目的地 合作執行。這可讓您保留特定目的地的狀態, 多個返回堆疊

如要以程式輔助方式執行這項操作,請在新增 popUpTo 時指定 saveState = true 您的導覽選項。

您也可以在導覽選項中指定 restoreState = true, 會自動還原返回堆疊和 目的地。

例如:

navController.navigate(
    route = route,
    navOptions =  navOptions {
        popUpTo<A>{ saveState = true }
        restoreState = true
    }
)

如要在 XML 中啟用儲存及還原狀態功能,請將 popUpToSaveState 定義為 true 在相關聯的 action 中分別以 restoreState 取代 true

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: Any = A
) {
    NavHost(
        modifier = modifier,
        navController = navController,
        startDestination = startDestination
    ) {
        composable<A> {
            DestinationA(
                onNavigateToB = {
                // Pop everything up to, and including, the A destination off
                // the back stack, saving the back stack and the state of its
                // destinations.
                // Then restore any previous back stack state associated with
                // the B destination.
                // Finally navigate to the B destination.
                    navController.navigate(route = B) {
                        popUpTo<A> {
                            inclusive = true
                            saveState = true
                        }
                        restoreState = true
                    }
                },
            )
        }
        composable<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() 的一般資訊,請參閱「使用選項導覽」指南。

使用動作彈出

使用動作進行導覽時,您可以視需要從返回堆疊中彈出其他目的地。舉例來說,如果應用程式有起始登入流程,則在使用者登入後,您應將所有與登入相關的目的地從返回堆疊中彈出,讓「返回」按鈕不會將使用者導回登入流程。

延伸閱讀

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

  • 循環導覽:瞭解如何在循環導覽流程中,避免過度填充返回堆疊。
  • 對話方塊目的地:瞭解就管理返回堆疊的方式而言,有哪些與對話方塊目的地相關的特殊注意事項。