儲存及管理導覽狀態

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

儲存返回堆疊

為提供良好的使用者體驗,請務必確保應用程式的導覽狀態在各種生命週期事件中保持不變,包括設定變更和程序終止。在 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)
}

記住含有 NavKey 子類型的返回堆疊

rememberNavBackStack 可組合函式會傳回 NavBackStack<NavKey>。如果應用程式定義自己的 NavKey 子類型,且所有索引鍵都繼承自該子類型,您可以實作自訂記住函式,保留該類型,如下所示:

@Serializable
sealed interface MyAppNavKey : NavKey

@Serializable
data object ScreenA: MyAppNavKey

@Serializable
data class ScreenB(val id: String): MyAppNavKey

@Composable
fun rememberMyAppNavBackStack(vararg elements: MyAppNavKey): NavBackStack<MyAppNavKey> {
    return rememberSerializable(serializer = serializer()) {
        NavBackStack(*elements)
    }
}

@Composable
fun MyApp() {
    // defaultNavBackStack is NavBackStack<NavKey>
    val defaultNavBackStack = rememberNavBackStack(ScreenA)
    // myAppNavBackStack is NavBackStack<MyAppNavKey>
    val myAppNavBackStack = rememberMyAppNavBackStack(ScreenA)
}

如需更多範例 (包括如何處理開放多型),請參閱 NavBackStackSamples。

替代方案:儲存在 ViewModel

管理返回堆疊的另一種方法是將其儲存在 ViewModel 中。如要在使用 ViewModel 或任何其他自訂儲存空間時,在程序終止後仍保留資料,您必須:

  • 確保金鑰可序列化:與 rememberNavBackStack 一樣,導覽金鑰必須可序列化。
  • 手動處理序列化和反序列化:當應用程式進入背景或還原時,您必須手動將每個金鑰的序列化表示法儲存至永久儲存空間 (例如 SharedPreferences、資料庫或檔案),並從中反序列化。

將 ViewModel 個執行個體的範圍限定為 NavEntry 個物件

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

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

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

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

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

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