儲存及管理導覽狀態

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

儲存返回堆疊

為提供良好的使用者體驗,請務必確保應用程式的導覽狀態在各種生命週期事件 (包括設定變更和程序終止) 中持續存在。在 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。這樣可確保 ViewModel 的狀態只會在特定 NavEntry 是返回堆疊的一部分時保留,並在彈出 NavEntry 時清除。

androidx.lifecycle:lifecycle-viewmodel-navigation3 外掛程式庫提供 NavEntryDecorator,可協助完成這項工作。這個修飾符會為每個 NavEntry 提供 ViewModelStoreOwner。當您在 NavEntry 內容中建立 ViewModel (例如在 Compose 中使用 viewModel()) 時,系統會自動將其範圍限制在返回堆疊中特定 NavEntry 的鍵。也就是說,NavEntry 會在新增至後堆疊時建立 ViewModel,並在移除時清除。

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

  1. androidx.lifecycle:lifecycle-viewmodel-navigation3 依附元件新增至 app/build.gradle.kts 檔案。
  2. 建構 NavDisplay 時,將 rememberSavedStateNavEntryDecorator() 新增至 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 { },
)