儲存及管理導覽狀態

以下各節將說明如何儲存返回堆疊,以及儲存與返回堆疊項目相關聯的狀態。

儲存返回堆疊

確保應用程式的導覽狀態在各種生命週期事件 (包括設定變更和程序終止) 中保持不變,是提供良好使用者體驗的關鍵。在 Navigation 3 中,您擁有返回堆疊,因此沒有關於如何建立或儲存返回堆疊的嚴格規範。不過,Navigation 3 提供便利的方法,可讓您取得可儲存的返回堆疊:rememberNavBackStack

使用 rememberNavBackStack

rememberNavBackStack 可組合函式的用途是建立返回堆疊,在設定變更和程序終止後仍會保留。

如要讓 rememberNavBackStack 正常運作,返回堆疊中的每個鍵都必須符合特定要求:

  • 導入 NavKey 介面:返回堆疊中的每個鍵都必須實作 NavKey 介面。這會做為標記介面,向程式庫發出信號,表示可以儲存金鑰。
  • 使用 @Serializable 註解:除了實作 NavKey,您也必須使用 @Serializable 註解標記重要類別和物件。

下列程式碼片段顯示 rememberNavBackStack 的正確實作方式:

@Serializable
data object Home : NavKey

@Composable
fun NavBackStack() {
    val backStack = rememberNavBackStack(Home)
}

替代做法:儲存在 ViewModel

管理返回堆疊的另一種做法,是將其儲存在 ViewModel 中。 如要在使用 ViewModel 或任何其他自訂儲存空間時,透過程序終止作業保留資料,請採取下列做法:

  • 確保金鑰可序列化:與 rememberNavBackStack 相同,導覽鍵必須可序列化。
  • 手動處理序列化和還原序列化作業:您必須負責將每個金鑰的序列化表示法手動儲存至永久儲存空間 (例如 SharedPreferences、資料庫或檔案),以便在應用程式進入背景或還原時使用。

範圍從 ViewModel 秒到 NavEntry

ViewModels 可用於在設定變更 (例如螢幕旋轉) 時保留 UI 相關狀態。根據預設,ViewModels 的範圍會設為最接近的 ViewModelStoreOwner,通常是 ActivityFragment

不過,您可能想將 ViewModel 範圍限定在返回堆疊中的特定 NavEntry (即特定畫面或目的地),而不是整個 Activity。這樣可確保只有在特定 NavEntry 屬於返回堆疊時,才會保留 ViewModel 的狀態,並在 NavEntry 彈出時清除狀態。

androidx.lifecycle:lifecycle-viewmodel-navigation3 外掛程式庫提供 NavEntryDecorator,可簡化這項作業。這個裝飾器會為每個 NavEntry 提供 ViewModelStoreOwner。在 NavEntry 的內容中建立 ViewModel 時 (例如在 Compose 中使用 viewModel()),系統會自動將其範圍限定在返回堆疊中該特定 NavEntry 的鍵。也就是說,NavEntry 新增至返回堆疊時,系統會建立 ViewModel,移除時則會清除。

如要使用 NavEntryDecoratorViewModel 範圍限定為 NavEntry,請按照下列步驟操作:

  1. app/build.gradle.kts 檔案中新增 androidx.lifecycle:lifecycle-viewmodel-navigation3 依附元件。
  2. 建構 NavDisplay 時,請將 rememberSaveableStateHolderNavEntryDecorator() 新增至 entryDecorators 清單。
  3. NavDisplay 中新增其他裝飾符。

NavDisplay(
    entryDecorators = listOf(
        // Add the default decorators for managing scenes and saving state
        rememberSceneSetupNavEntryDecorator(),
        rememberSavedStateNavEntryDecorator(),
        // Then add the view model store decorator
        rememberViewModelStoreNavEntryDecorator()
    ),
    backStack = backStack,
    entryProvider = entryProvider { },
)