### Navigation 3 推出的背景與目的 * 提供更簡潔、符合 Compose 思維的 API * 解決舊版 navigation 無法直接控制 backstack 的問題 * 支援多畫面(Multi-pane)場景,適用於平板、桌面與 Web * 為多平台(Kotlin Multiplatform)而設計,未來將強化 Web 支援 --- ### Backstack 與 Key 的概念 * Backstack 是一個記錄畫面順序的資料結構,內容是 key 而非實體畫面 * Key 為可序列化的資料類別,需實作 NavKey 介面 * Backstack 可用 rememberNavBackstack 建立,並能保存與還原狀態 * 管理 backstack 僅需操作 List 結構,如新增或移除元素 --- ### NavDisplay 取代 NavHost * NavDisplay 是導航的主要容器,需傳入 backstack * 不再需要 NavController,改由直接操作 backstack * NavDisplay 接收 entryProvider,決定要渲染哪個畫面 * 可傳入 entryDecorators 添加額外行為(如 ViewModel 支援、狀態保存) --- ### EntryProvider 的使用方式 * 接收 key 並根據其型別選擇要顯示的畫面 * 每個畫面需用 NavEntry 包裝,並傳入對應的 key 與 Composable * 可使用 DSL 替代 when 判斷,使語法更簡潔 * 如無符合的 key,建議拋出例外避免錯誤畫面顯示 --- ### 實作畫面與導覽流程 * 建立 NoteListScreen 作為起始畫面,顯示列表項目 * 建立 NoteDetailScreen 並接受 ID 參數,用於顯示指定筆記內容 * 當列表項目被點擊時,將 NoteDetailScreen 實例加到 backstack 中即完成導覽 * 返回時移除最後一個 backstack 元素即可回上一頁 --- ### ViewModel 的支援方式 * 可使用 ViewModel 搭配 Navigation Tree 提供的 decorator 進行畫面綁定 * ViewModel 可依據單一 nav entry 綁定生命週期 * 當畫面被移除出 backstack 時,ViewModel 也會一併清除 --- ### 自定義 Entry Decorators * Decorator 是擴充行為的機制,能攔截並處理每一次導航邏輯 * 官方提供 ViewModel、狀態保存、Scene 支援等預設 decorators * 可自定義 decorator,類似 Retrofit 的 Interceptor 設計模式 --- ### 總結 Navigation 3 的關鍵特色 * 完整控制 backstack,結構清晰、操作直覺 * 不依賴 NavController,導覽邏輯更靈活 * 更好整合 ViewModel 與畫面狀態 * 更適合複雜畫面、跨平台與大型專案使用 * API 設計簡潔,貼近 Compose 架構核心理念 --- ### 建立 Note 資料類別與樣本資料 * 建立 `Note` 資料類別,包含 id、title、content 和 color。 * 產生 100 筆樣本筆記,並隨機產生顏色,設定透明度避免文字難以閱讀。 --- ### 顯示 Note 列表畫面(NodeListScreen) * 使用 `LazyColumn` 顯示樣本筆記列表。 * 每項筆記以 `Column` 呈現,內含 title 和 content。 * 點擊筆記時觸發 `onNoteClick` callback 傳遞筆記 ID。 --- ### 設定導覽邏輯與 Backstack 操作 * 在 `NavigationRoot` 中接收筆記 ID,透過 `backstack.add` 推送對應的 `NoteDetailScreen`。 * 移除畫面則使用 `backstack.removeLast()`,等同舊版 `popBackStack()`。 * 示範可對 backstack 進行任意操作(如重排、重複推入、移除)以驗證靈活性。 --- ### 使用 ViewModel 顯示筆記詳情 * 建立 `NoteDetailViewModel`,內含 `MutableStateFlow<Note>` 用於儲存當前筆記。 * 不再從 `SavedStateHandle` 取得導航參數(在 alpha 階段不支援)。 * 改為透過建構子明確接收參數(如筆記 ID)。 --- ### 與 Koin 整合 ViewModel 與參數注入 * 使用 `viewModelOf()` 註冊 ViewModel 至 Koin。 * 建立 Application 類別啟用 Koin,並於 `AndroidManifest.xml` 中註冊。 * 使用 `getViewModel(parameters = { parametersOf(id) })` 傳遞參數給 ViewModel。 --- ### 在 Composable 中觀察 ViewModel 狀態 * 使用 `collectAsStateWithLifecycle()` 觀察 ViewModel 的筆記資料。 * 將筆記內容渲染在詳情畫面上,顯示 title、content 與背景色。 --- ### 將 Backstack 儲存在 ViewModel 中(進階) * 可將 `MutableStateList<NavKey>` 存於 ViewModel 中,提升測試與控制能力。 * Backstack 須自行處理儲存與還原(process death 時會遺失狀態)。 * 儲存方式可透過檔案或 SharedPreferences 進行序列化還原。 * 好處是可在單元測試中驗證導航邏輯,無需啟動 UI。 --- ### 系統行為與備註 * 使用 `rememberNavBackstack()` 時,系統會自動保存與還原 backstack 狀態。 * 若使用自訂 backstack(如放入 ViewModel),需自行處理恢復流程。 * 示範透過模擬 process death(Logcat 中 kill process),系統能還原畫面。 --- ### Scenes 基本概念 * Scenes 是 Navigation 3 的新功能,用來組合多個畫面同時顯示 * 每個畫面仍維持獨立狀態與 ViewModel * 適用於平板、橫向裝置等大螢幕情境 * 常見用法是 two-pane(列表 + 詳細畫面) --- ### Dialog Scene 使用方式 * 內建支援對話框形式的場景呈現 * 利用 metadata 設定該畫面以 Dialog 顯示 * 使用 `dialogSceneStrategy` 套用邏輯 * Dialog Scene 可同時顯示兩個畫面(主畫面與對話框) --- ### 自訂 Two-pane Scene * 建立自訂 Scene 類別 TwoPaneScene,實作 `Scene<T>` * 使用 Row 佈局呈現左右兩個畫面 * 左右畫面皆以 nav entry 顯示,可自訂寬度比重 * 保持每個畫面邏輯獨立、支援單獨 ViewModel --- ### 自訂 Scene Strategy 判斷條件 * 建立 TwoPaneSceneStrategy,實作 `SceneStrategy<T>` * 使用 WindowSizeClass 檢查是否為中等以上寬度裝置 * 當 backstack 最後兩項 metadata 含有 twoPane 標記且裝置符合寬度要求時套用 two-pane scene * 回傳 previousEntries 決定返回行為,通常為移除最上層畫面後的狀態 --- ### 實作限制與錯誤現象 * 在平板或橫向時 two-pane 可正常顯示列表與詳細畫面 * 點擊其他項目進行導航時會導致 crash * 錯誤訊息為 nav key 重複使用 * 該錯誤也發生在 Google 官方範例中,非使用者實作問題 --- ### 動畫與返回手勢支援 * 支援過場動畫與返回動畫設定 * 可設定 `transitionSpec`、`popTransitionSpec`、`predictivePopTransitionSpec` * 預測性返回動畫(predictive back)可顯示導航目標預覽 --- ### 現階段功能限制 * 尚未支援 Nested Nav Graph 功能 * `NavEntryWrapper` 無法處理多個畫面群組 * 尚未提供深度連結(deep link)內建支援 * 若需處理 deep link,需自行處理 intent 並控制 backstack --- ### Terminology * **Navigation 3**:Jetpack 最新的 Compose 導航函式庫,重構設計、去除 `NavController`,改以 `NavDisplay` 和 Backstack 控制導覽流程。 * **NavDisplay**:新版的導航入口 Composable,取代傳統的 `NavHost`,接收 `backstack` 與 `entryProvider`。 * **Backstack**:開發者可自建的可變堆疊資料結構,用於控制目前與歷史畫面堆疊。 * **NavKey**:標示為可作為導覽目的地的類型,所有 Backstack 中的元素都需實作此介面。 * **Serializable Annotation**:標記 `NavKey` 類別可序列化,便於儲存與還原導覽狀態。 * **rememberNavBackstack**:建構並記憶導覽 Backstack 的 Composable,支援狀態保存與還原。 * **EntryProvider**:依據 `NavKey` 提供對應的 `NavEntry`,用於繪製實際 Composable 畫面。 * **NavEntry**:封裝實際畫面的物件,含畫面鍵值 (`key`) 與 Composable 畫面內容。 * **EntryDecorators**:套用至 `NavDisplay` 的裝飾器,可擴充導航行為(如 ViewModel 管理、狀態保存)。 * **rememberViewModelStoreNavEntryDecorator**:將 ViewModel 的生命週期綁定至單一畫面 (`NavEntry`)。 * **rememberSavedStateNavEntryDecorator**:支援畫面狀態的保存與還原(如旋轉後還原 Backstack)。 * **rememberSceneSetupNavEntryDecorator**:設定場景顯示行為,如多畫面配置(後續可能應用於多窗格 UI)。 * **Composable Navigation**:畫面以 Composable 函數表示,並透過鍵值切換顯示內容。 * **Type-Safe Navigation**:以型別安全的方式管理畫面與其參數(利用 `data class` 表示畫面與傳遞資料)。 * **NavKey Matching**:使用 `when` 或 DSL 方式比對 `NavKey` 並渲染對應畫面。 * **Composable Routing via NavKey**:畫面導覽以 `NavKey` 為基礎,不再依賴 route 字串。 * **Dynamic Backstack Manipulation**:可依任意邏輯新增、刪除或重排序 `Backstack` 元素。 * **Initial Backstack Configuration**:可自訂應用啟動時的初始 Backstack 內容。 * **Decoupled Navigation Logic**:導覽行為不再綁定至 Composable 本身,利於模組重用。 * **Multiplatform Compatibility**:支援 Compose Multiplatform,與 iOS、Web 架構兼容性設計。 * **WindowSizeClass**:可偵測裝置大小分類,便於針對不同螢幕尺寸設計導覽行為。 * **Adaptive Navigation**:支援不同尺寸與平台上適應式的導覽行為與 UI 呈現。 * **Modifier Forwarding**:允許將修飾器向下傳遞至內部元件,保持版面一致性。 * **Manual Pop Behavior**:不再有 `popBackStack`,需手動從 Backstack 刪除頂層項目。 * **Composable Entry Point**:每個畫面透過 `NavEntry` 封裝作為進入點進行顯示。 * **Composable List Navigation**:可為清單項目建立對應的 `NavKey` 並切換畫面至詳細頁。 * **Metadata in NavEntry**:可選擇性為 `NavEntry` 添加額外資料,支援進階畫面需求。 * **Exhaustive NavKey Matching**:完整列舉每一個 `NavKey` 類型,以確保畫面轉換邏輯明確。 * **NavEntry Lifecycle Scoping**:每個 `NavEntry` 可擁有獨立生命週期管理(例如 ViewModel)。 * **State Restoration after Process Death**:程式重啟後自動還原導覽狀態,提升使用者體驗。 * **EntryProvider DSL**:DSL 形式的 `entryProvider` 提供更簡潔清晰的畫面定義。 * **Flexible Start Destination**:可任意指定初始畫面,包含帶參數的畫面。 * **Navigation Logic as Data**:將導覽定義轉換為資料(key)操作,提升測試與預測性。 * **Multiple NavDisplays**:可依需求同時定義多組 `NavDisplay`,支援多畫面結構。 * **Scene Support**:實驗性場景功能(可能支援動畫切換、多窗格畫面等進階 UI)。 * **EntryDecorator Customization**:開發者可自行實作裝飾器,自訂導覽行為(類似攔截器)。 * **BackStack UI Decoupling**:UI 呈現與 Backstack 邏輯完全分離,利於模組化與單元測試。 * **Composable Injection via Koin**:與 Koin 整合支援 Composable 中的依賴注入與 ViewModel 注入。 * **JetBrains Kotlin Serialization Plugin**:使用 `kotlinx.serialization` 處理參數序列化與還原。 * **Mutable State List for Backstack**:Backstack 為 Compose 的 `SnapshotStateList`,支援狀態觀察。 * **Activity-Level Scoping(ViewModel)**:預設 ViewModel 為 Activity 層級,可透過 Decorator 降階。 * **Compose-Only Navigation Paradigm**:完整移除 XML,全部導航邏輯與 UI 行為皆於 Compose 完成。 * **Composable Interoperability**:新導航架構與 Compose 所有特性(狀態、生命周期)高度整合。 * **Scalable Navigation System**:適合中大型應用,支援動態 Backstack 管理與高彈性路由控制。 * **Compose-First Declarative API**:設計上完全以 Compose 宣告式程式風格為優先,簡化開發心智負擔。 * **Note Data Class**:用於表示筆記資料的資料類別,通常包含 ID、標題、內容與顏色等屬性。 * **Color Alpha**:使用 `.copy(alpha = 0.5f)` 設定顏色透明度,避免背景顏色影響文字可讀性。 * **Sample Notes**:以亂數產生 100 筆測試用的筆記清單,常用於 UI 測試與展示。 * **LazyColumn**:Jetpack Compose 提供的高效能垂直列表組件,用來顯示多筆資料。 * **Modifier.fillMaxWidth()**:設定組件寬度填滿可用空間。 * **Clickable Modifier**:讓某個 Composable 可點擊,並觸發對應的 lambda 回調。 * **Composable Callback Navigation**:透過 lambda 傳遞點擊事件至上層導覽邏輯,保持畫面模組的解耦。 * **Backstack.addLast()**:將新的 `NavKey` 加入堆疊頂端,相當於進行畫面導覽。 * **Backstack.removeLastOrNull()**:從堆疊中移除最上層的畫面,對應於返回上一畫面。 * **Backstack.shuffle()**:任意打亂 backstack 中的畫面順序,雖非實務常用但展現高度彈性。 * **NavKey Naming Conflict**:避免畫面資料類與畫面 UI 函數同名,建議以 `UI` 後綴區分。 * **ViewModel Integration**:在每個畫面中透過 ViewModel 處理邏輯與狀態管理。 * **ViewModelScope Lifecycle**:藉由 decorator 將 ViewModel 的生命週期綁定至特定畫面 (`NavEntry`)。 * **SafeStateHandle 缺失**:在 Navigation 3 中不再預設注入 `SafeStateHandle`,需改為手動傳參。 * **Koin ViewModel Injection**:使用 Koin 進行 ViewModel 的依賴注入與參數初始化。 * **Koin Parameters Injection**:透過 `parametersOf(...)` 明確傳入 ViewModel 所需參數。 * **Android Application Class**:用來初始化 Koin 或其他應用層級設定。 * **Nav Argument via ViewModel Constructor**:透過建構式參數將畫面資料傳遞給 ViewModel。 * **MutableStateFlow for UI State**:在 ViewModel 中以 `MutableStateFlow` 管理與曝露 UI 狀態。 * **collectAsStateWithLifecycle()**:Compose 與 Flow 整合,且考慮生命週期的狀態收集函式。 * **@Serializable NavKey**:所有用於導覽的鍵必須實作 `NavKey` 並標註為 `@Serializable`。 * **ViewModel Navigation Context**:ViewModel 可擁有 backstack,進而掌握目前畫面與導覽邏輯。 * **Persistent Backstack via RememberNavBackstack**:使用此函式自動處理導覽堆疊的保存與還原。 * **Manual Backstack Persistence**:若將 backstack 移入 ViewModel,需自行序列化並保存狀態。 * **SnapshotStateList**:Compose 提供的可觀察 List 型狀態容器,支援資料變動自動更新 UI。 * **Testing Navigation in ViewModel**:允許在單元測試中模擬與驗證畫面導覽邏輯。 * **Composable-based Argument Propagation**:導覽參數不再自動注入,需開發者明確提供。 * **Global Navigation ViewModel**:若需全域導覽控制,可建立一個管理整體 backstack 的 ViewModel。 * **Preference/Local Storage Backstack Saving**:將 backstack 存入偏好設定或檔案,支援進階還原需求。 * **Compose Navigation Debugging**:可視化與操作 backstack,協助開發與除錯。 * **Navigation Flexibility via Data Structure**:將導航邏輯轉為資料結構操作(如 list),提高彈性。 * **ViewModel Reusability Across Screens**:能依據不同畫面重複使用 ViewModel 並注入不同參數。 * **Composable ↔ ViewModel Clear Separation**:畫面僅處理顯示,邏輯完全由 ViewModel 控制。 * **Koin Integration Best Practice**:明確注入所需型別參數,避免 ViewModel 無法正確建構。 * **OnCreate Application DI Bootstrapping**:於應用啟動時進行 DI 框架初始化。 * **Color.fromLong()**:從 `Long` 整數轉為 Compose 支援的 `Color`。 * **FontSize with sp()**:字體大小使用 `sp` 單位,符合使用者系統偏好設定。 * **Composable Padding & Spacing**:以 `padding` 與 `Spacer` 控制畫面間距與留白。 * **Composable Preview with Data**:使用靜態資料作為 Compose 畫面預覽來源。 * **State Restoration Testing via Kill Process**:透過模擬殺死程序,測試狀態保存與導覽還原。 * **Custom NavEntry Behavior via Decorators**:自訂畫面生命週期與附加行為(例如 ViewModel 清理)。 * **Navigation Parameter Testing**:驗證傳遞參數後畫面與資料是否正確更新。 * **DI Module Structure**:建立清晰模組化的依賴注入架構,如 `AppModule`。 * **ViewModel Initialization Strategy**:依據參數是否複雜選擇預設建構或手動注入。 * **NavKey Routing Granularity**:每個畫面皆有明確鍵值與資料來源,利於可測試性與重用性。 * **In-memory Static Content Limitation**:靜態資料如列表於重建後變化,可反映為狀態保存範例。 * **Process Death & Restoration Awareness**:開發者需理解 Android 系統行為並妥善應對。 * **Clear Exception on Missing Match**:明確拋出例外而非使用 `else` 處理,增強可讀性與除錯能力。 * **Composable Name Conflict Resolution**:推薦將 Composable 函數名稱與資料類名稱區分。 * **Compose Function Rename via Shift + F6**:利用快捷鍵避免重名與 IntelliJ 衝突。 * **ViewModel Injection via DI Framework**:簡化初始化,避免手動建構依賴。 * **Backstack as a Navigation State Source**:導覽不再由控制器主導,而是由狀態變動驅動畫面更新。 * **Scenes**:在 Navigation 3 中,Scene 是一種組合畫面的方式,可將多個畫面組合在一起顯示(如對話框、多欄畫面)。 * **TwoPane Scene**:特定的 Scene 類型,將兩個畫面並排顯示,常用於平板裝置或寬螢幕。 * **Dialog Scene Strategy**:將畫面以對話框形式呈現的策略,常搭配對話場景使用。 * **Scene Strategy**:用來定義在什麼情況下套用哪一種 Scene,如依螢幕尺寸選擇單欄或雙欄。 * **Scene Metadata**:用於標註特定畫面是否符合某 Scene 條件的標記資料(例如兩欄或對話框)。 * **Row + Modifier.weight()**:在 Scene 中以 `Row` 配合 `weight` 決定畫面分配寬度比例。 * **NavEntry**:單一畫面的實例與內容包裝,可用於 Scene 組合。 * **Previous Entries**:定義當使用者按返回鍵時 Scene 該如何回退(通常回到上一個 Entry)。 * **Scene.content()**:定義自訂 Scene 如何顯示內容,實作畫面排列邏輯。 * **CurrentWindowAdaptiveInfo**:提供當前裝置的視窗資訊,判斷是否為平板或橫向。 * **Breakpoint.Dp.Medium**:代表中等尺寸的螢幕界線,常用於切換單欄與雙欄場景。 * **entries.dropLast(1)**:移除 backstack 最後一個元素,用以指定 Scene 的 previousEntries。 * **entries.takeLast(2)**:取 backstack 的最後兩個畫面,判斷是否要合併顯示為 Scene。 * **Scene Strategy.calculateScene()**:決定是否與如何套用 Scene 的邏輯方法。 * **NavEntry.content(key)**:呼叫特定 Entry 的內容顯示 Composable。 * **DialogProperties**:自訂對話框樣式屬性,例如不可取消、模糊背景等。 * **Scene Companion Object**:集中管理 Scene 所需常數與 metadata keys。 * **Scene Metadata Map**:每個畫面傳遞額外資料的方式,用於判斷是否加入某 Scene。 * **Predictive Back Animation**:Android 14+ 的返回預覽手勢動畫,Navigation 3 提供支援。 * **TransitionSpec**:自訂畫面切換動畫規格的方式,包括 `popTransitionSpec`。 * **PredictivePopTransitionSpec**:針對返回預覽提供動畫過渡效果。 * **NavDisplay.transitionSpec**:在 Navigation 3 的核心組件中設置轉場動畫。 * **Scene Invocation on Tablet**:當裝置為平板並符合條件時,Scene 自動生效顯示多畫面。 * **Backstack Key Conflict Bug**:目前場景下存在 key 重複錯誤問題,為已知 alpha 版本限制。 * **Scene State Sharing**:允許每個畫面獨立使用 ViewModel,同時在一個場景中共存。 * **ViewModel Scope in Scenes**:即使畫面同時顯示,每個畫面仍擁有自己生命週期綁定的 ViewModel。 * **Deep Link Manual Handling**:目前需手動處理深連結,例如使用 `onNewIntent()`。 * **NavEntryWrapper**:提供對單一 `NavEntry` 的額外封裝,但不支援多畫面整合。 * **No Nested Graph Support**:目前未提供類似舊版 Nested Graph 的群組畫面與共享 ViewModel 支援。 * **Navigation Bug Traced to Google Sample**:相同錯誤亦出現在 Google 原始碼中,推測為底層問題。 * **Compose Scene Flexibility**:開發者可自定 Scene 排列樣式,例如橫排、網格、重疊等。 * **Grid Layout Scene (2x2)**:Scene 可擴展成多欄或網格顯示應用場景。 * **Metadata-driven Scene Application**:Scene 應用完全取決於 metadata 與 strategy 評估。 * **Single Scene Strategy Limitation**:每次只能設一個 sceneStrategy,需在其中整合多種邏輯。 * **Adaptive Layout Responsiveness**:自動依螢幕大小切換場景設計,提高跨裝置兼容性。 * **Composable Independence in Scene**:即便畫面同時呈現,每個畫面 Composable 可單獨開發與測試。 * **Scene-centric Navigation Modeling**:導覽模式轉為以場景為核心組合,而非單一畫面跳轉。 * **Backstack-as-State Foundation**:場景變換建立在 backstack 這個可觀察狀態資料結構上。 * **NavEntry.key Uniqueness Required**:每個畫面需保證唯一 key,以避免重複錯誤。 * **Scene Conflict Error Trace**:常見錯誤提示為 “Key used multiple times”,與 metadata 設定衝突有關。 * **Orientation-based Scene Switching**:以橫/直向切換 Scene 排列方式,增進 UX。 * **Jetpack Compose Recomposition Control**:Scene 排列可避免不必要重組,提高效能。 * **ViewModel Retention per NavEntry**:每個 NavEntry 與 ViewModel 綁定可精準管理狀態與生命週期。 * **Composable Isolation in Layout**:Scene 實現時保持 Composable 相互隔離,避免邏輯耦合。 * **Composable Weight for Flexible Layout**:使用 `weight` 控制 Scene 組件的空間分配。 * **Back Handling per Scene**:Scene 可自訂返回邏輯,如同時關閉兩畫面或只退一層。 * **Preview-friendly Scene Architecture**:可將 Scene 作為單元獨立預覽與測試。 * **Testing Scene Transitions**:Scene 可配合 UI 測試驗證排列與互動邏輯。 * **Future-proof Layout Design**:Scene 設計便於擴充應對未來 UI 需求(折疊裝置等)。 * **Adaptive + Scene Synergy**:結合 Adaptive Layout 判斷與 Scene 排列,實現多裝置最佳體驗。