---
# System prepended metadata

title: Android 筆記 – Navigation (二) 使用 Safe Args 傳遞資料
tags: [傳遞, android, Safe, Args, 資料傳遞, navigation, 引數]

---

<h1>Android 筆記 – Navigation (二) 使用 Safe Args 傳遞資料</h1>

>前置閱讀:[Android筆記–Navigation導航組件](https://hackmd.io/@9YAtszqXS2OLNZOrLY_-Jg/android_navigation)

==<code>SafeArgs</code> 專門用來在 Navigation 中參數傳遞，並且同時能提供類型安全和編譯時檢查==，是 <code>Android Jetpack Navigation 組件</code> 的擴展，旨在增強 Fragment 或 Activity 之間導航時的類型安全性。==它利用生成的類型安全類別來取代手動構建 Bundle，從而減少錯誤並提高可讀性與可維護性==。

<font size=4><b>&lt; <u>特點</u> &gt;</b></font>
- <u>類型安全</u>: <code>SafeArgs</code> 生成的代碼會自動檢查參數的類型，避免了傳遞錯誤類型或因手動鍵入參數名稱錯誤而引發的崩潰
- <u>編譯時檢查</u>: <code>SafeArgs</code> 會在編譯時檢查導航操作和參數設置是否正確，減少因執行時錯誤造成的問題。
- <u>自動生成代碼</u>: 通過 <code>SafeArgs</code>，Android Studio 會自動生成導航操作和參數的代碼，大幅減少手動處理的工作量。
  
<font size=4><b>&lt; <u>工作原理</u> &gt;</b></font>
- <code>SafeArgs</code> 會為每個動作產生的目的地產生類別。產生的類別名稱會在原始目的地的類別名稱中加入「Directions」（路線）。舉例來說: 
  - 如果原始目的地的名稱為 「AAAFragment」
      ↓
  - 則產生的類別名稱為 「AAAFragmentDirections」。
- 產生的類別會為原始目的地中定義的每種動作提供靜態方法。此方法會將任何已定義的動作參數當做引數，並傳回一個 <code>NavDirections 物件</code>，您就能將此物件直接傳遞到 <code>navigate()</code>。
- 命名法類似於 <code>ViewBinding</code>

| 特性 | <code>ViewBinding</code> | <code>SafeArgs</code> |
| --- | --- | --- |
| **功能** | 自動生成與 XML 佈局檔案相關的類別，用於綁定 View | 自動生成與導航動作及參數相關的類別，用於導航 |
| **生成邏輯** | 生成名稱為 `{佈局名稱 + Binding}` 的類別 | - 生成名稱為 `{Fragment名稱 + Directions}` 的類別。</br> - 生成名稱為 `{Fragment名稱 + Args}` 的類別|
- `{Fragment名稱 + Directions}`: 根據導航途中的`<action>標籤`生成，用於建立導航動作和設定參數
- `{Fragment名稱 + Args}`: 根據目的地是否需要接收參數來生成，用於在目的地 Fragment 中獲取參數
      
---
▼宣告依賴項
---
>[!Warning]注意事項
><code>SafeArgs</code> 需要分別在 <code>頂層 build.gradle</code> 和 <code>應用程式層的 build.gradle</code> 寫入依賴項


到官網尋找依賴項[AndroidDeveloper--Safe Args
](https://developer.android.com/guide/navigation/use-graph/safe-args?hl=zh-tw#enable)

<details><summary>圖解</summary>
<div>

```groovy=
//頂層模組的依賴項
buildscript {
    repositories {
        google()
    }
    dependencies {
        val nav_version = "2.8.0"
        classpath("androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version")
    }
}
```
```groovy=
//應用程式層級的依賴項，二擇一即可。
//產生適合Kotlin 模組的 Kotlin 語言程式碼
plugins {
    id("androidx.navigation.safeargs.kotlin")
}
//產生適合 Java 或混用 Java 及 Kotlin 模組的 Java 語言程式碼
plugins {
    id("androidx.navigation.safeargs")
}
```
![image](https://hackmd.io/_uploads/ryJotqsG1l.png)

</div>
</details>

<br>

>延伸閱讀:[Android筆記–SafeArgs補充–為甚麼SafeArgs的依賴要用到ClassPath?
](https://hackmd.io/@9YAtszqXS2OLNZOrLY_-Jg/android_Navigation_SafeArgs_AdditionalInformation)


---
▼簡單使用
---
<code>SafeArgs</code> 的使用要點主要有三部分: 
<details><summary>1. 導航圖設定</summary>
<div>
<br>
    
首先到導航圖中設定目標 Fragment 所要接收的參數
    
---
1. 到導航圖中選擇一個要作為目標的 Fragment，在 Attribute 面板中的 Arguments 中點選加號後進行設定
  ![image](https://hackmd.io/_uploads/ByOR9HhMye.png)
2. 設定完成後，Arguments 就會被附加到導航圖中的目標 Fragment 的設定之中
  ![image](https://hackmd.io/_uploads/BJME2S2Gkg.png)
>[!Important]
>- 這個設定是多對一的，也就是說，所有「目的地為此 Fragment 的 起點 Fragment」，都必須要提交我們設定在目標 Fragment 上的 Argument 才能過來
>- 例如: 只要是以 FragmentB 為目標的 Fragment(例如: FragmentA、C、D)，都必須提供一個 <code>String 參數</code> <code>nameBBB</code>，否則會出現錯誤

![image](https://hackmd.io/_uploads/SkmOkU3G1g.png)

</div>
</details>
<details><summary>2. 在起點 Fragment 中放入參數</summary>
<div>
<br>
    
在起點 Fragment 中呼叫 「XXXDirections」，並選擇對應的 「actionXXToXX」，然後填入 「步驟1」 中所設計的參數，完成後，可將此物件提供給 <code>NavController</code> 當作 [Action](https://hackmd.io/@9YAtszqXS2OLNZOrLY_-Jg/android_navigation#NavigationGraphBasicProperties) 使用
    
---
![image](https://hackmd.io/_uploads/SkrKvU3Mkx.png)

```kotlin=
class FragmentA : Fragment() {
    private lateinit var binding: FragmentABinding
    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,savedInstanceState: Bundle?): View? {
        binding = FragmentABinding.inflate(inflater, container, false)

        val navController = findNavController()
        val action = FragmentADirections.actionFragmentAToFragmentB("String A To B")

        binding.button.setOnClickListener {
            navController.navigate(action)
        }
        return binding.root
    }
}
```

</div>
</details>
<details><summary>3. 在目標 Fragment 中接收參數</summary>
<div>
<br>
    
呼叫 「<code>Fragment 名 + Args 物件</code>」，然後使用其中的 <code>fromBundle(requireArguments())</code> 來獲取 <code>Arg</code>，再從 <code>Arg</code> 中獲取需要的參數
    
---
![image](https://hackmd.io/_uploads/S1jX7-azye.png)

```kotlin=
class FragmentB : Fragment() {
    private lateinit var binding: FragmentBBinding
    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,avedInstanceState: Bundle?): View? {
        binding = FragmentBBinding.inflate(inflater, container, false)

        val arg = FragmentBArgs.fromBundle(requireArguments())
        binding.tvBB.text = arg.nameBBB

        return binding.root
    }
}
```

</div>
</details>

---
▼Q&A
---
<details><summary>如果我 A 有設定 <code>Argument</code>，然後從 B 來 <code>popUpToA</code> 的話，這樣 A 可以拿到資料嗎?</summary>
<div>

---
不行，因為 <code>popUpTo</code> 的本質是清空導航堆疊的一部分，並彈回到指定的目的地，這個操作並不涉及資料傳遞。

</div>
</details>


---
▼GitHub 專案
---
[MyPratice_Navigation_SafeArgs_1](https://github.com/PudCheetah/MyPratice_Navigation_SafeArgs_1)

---
- 上一篇: [Android筆記–Navigation(一)導航組件](https://hackmd.io/@9YAtszqXS2OLNZOrLY_-Jg/android_navigation)
- 下一篇: [Adnroid筆記--Navigation(三)AppBarConfiguration](https://hackmd.io/@9YAtszqXS2OLNZOrLY_-Jg/android_AppBarConfiguration)
---
▼參考資料
---
- [AndroidDeveloper--Safe Args](https://developer.android.com/guide/navigation/use-graph/safe-args?hl=zh-tw)
  - [AndroidDeveloper--在目的地之間傳遞資料](https://developer.android.com/guide/navigation/use-graph/pass-data?hl=zh-tw)
- [AndroidX — 如何使用Navigation的 Safe Args更安全的傳遞兩個Fragment的參數](https://jefflin1982.medium.com/androidx-%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8navigation%E7%9A%84-safe-args%E6%9B%B4%E5%AE%89%E5%85%A8%E7%9A%84%E5%82%B3%E9%81%9E%E5%85%A9%E5%80%8Bfragment%E7%9A%84%E5%8F%83%E6%95%B8-4e6bbab05472)
- [[Day12] Android - Kotlin筆記：JetPack - Fragments在Navigation中的參數傳遞(Safe Args)](https://ithelp.ithome.com.tw/articles/10264464)
