やらかし：AndroidのNavigationコンポーネントのSafe Argsを使っててnavArgs()がUnresolved referenceになった

# 結論
結果から言うとただの勘違いでした。

:::note warn
実装方法は間違えてないのに解決できない方は<a href="#実装は正しいのに解決できない場合">こちら</a>
:::

# 起こったこと
``` kotlin:DetailFragment.kt
class DetailFragment : Fragment() {
    companion object {
        private val args: DetailFragmentArgs by navArgs()  // navArgs Unresolved reference.

        fun newInstance() {
            // ...略
        }
    }
    private var _binding: DetailFragmentBinding? = null
    private val binding get() = _binding!!

    // ...略
}
```
何さがしてもUnresolved referrenceが解決できなかった。

# 解決
ただ単に、`companion object`に内包していたのが原因だったって落ちです
正解はこう

``` kotlin:DetailFragment.kt
class DetailFragment : Fragment() {
    private var _binding: DetailFragmentBinding? = null
    private val binding get() = _binding!!
    private val args: DetailFragmentArgs by navArgs()  // class直下に記入

    // ...略
}
```
そもそも`Safe Args`使う場合は`newInstance()`とか必要ない場合が多いので書き残してるのに引っ張られてcompanion objectに書いてしまったのが問題でした。

# 実装方法
他の方が実装方法書いてるので必要ないかもしれないですが、備忘録的に書いておきます。

#### プロジェクト下のbuild.gradle

``` gradle:build.gradle(project)
buildscript {
    // ...略
    ext.nav_version = '2.3.5'
    repositories {
        google()
        mavenCentral()
    }
    dependencies {
        classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version"  // Safe Argsの有効化
        // ...略
    }
}
```
#### app下のbuild.gradle
``` gradle:build.gradle(app)
plugins {
    id 'kotlin-android'
    id 'kotlin-kapt'
    id 'androidx.navigation.safeargs.kotlin'  // Kotlinのみ場合はこちら
//  id 'androidx.navigation.safeargs'         // JavaとKotlin混雑の場合はこちら
    // ...略
}

// ...略

dependencies {
    implementation "androidx.navigation:navigation-fragment-ktx:$nav_version"
    implementation "androidx.navigation:navigation-ui-ktx:$nav_version"
    // ...略
}
```

#### Navigation graph
[**File**] > [**New File**] > [**Android Resource File**]
[**File name**]は任意（Android公式ではnav_graph）
[**Resource type**]は**Navigation**
パスは res/navigation/ になるけど、ビルドタイプに合わせて変更可能

``` xml:nav_graph.xml
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/nav_graph"
    app:startDestination="@id/startFragment">

    <fragment
        android:id="@+id/mainFragment"
        android:name="com.sampl.MainFragment"
        android:label="start_fragment"
        tools:layout="@layout/start_fragment">

        <!-- アニメーションはここに記入 -->
        <action
            android:id="@+id/action_startFragment_to_firstFragment"
            app:destination="@id/startFragment"
            app:enterAnim="@android:anim/fade_in"
            app:exitAnim="@android:anim/fade_out"
            app:popEnterAnim="@android:anim/fade_in"
            app:popExitAnim="@android:anim/fade_out" />
        <action
            android:id="@+id/action_startFragment_to_secondFragment"
            app:destination="@id/secondFragment"
            app:enterAnim="@android:anim/fade_in"
            app:exitAnim="@android:anim/fade_out"
            app:popEnterAnim="@android:anim/fade_in"
            app:popExitAnim="@android:anim/fade_out" />
        <action
            android:id="@+id/action_startFragment_to_dialogFragment"
            app:destination="@id/dialogFragment" />
    </fragment>
    <fragment
        android:id="@+id/firstFragment"
        android:name="com.sample.FirstFragment"
        android:label="first_fragment"
        tools:layout="@layout/first_fragment">
        <action
            android:id="@+id/action_firstFragment_to_startFragment"
            app:destination="@id/startFragment" />
    </fragment>
    <fragment
        android:id="@+id/secondFragment"
        android:name="com.sample.SecondFragment"
        android:label="second_fragment"
        tools:layout="@layout/second_fragment">
        <action
            android:id="@+id/action_secondFragment_to_startFragment"
            app:destination="@id/secondFragment" />
        <!-- Safe Args -->
        <!-- 引数はここで宣言 -->
        <argument
            android:name="num"
            app:argType="integer" />
    </fragment>
    <!-- DialogFragmentはdialogを記入 -->
    <dialog
        android:id="@+id/dialogFragment"
        android:name="com.sample.DialogFragment"
        android:label="DialogFragment"
        tools:layout="@layout/dialog_fragment" />
</navigation>
```
#### 各Fragment
``` kotlin:StartFragment.kt
class StartFragment: Fragment() {
    private var _binding: StartFragmentBinding? = null
    private val binding get() = _binding!!

    // ...略

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        binding.button_first1.setOnClickListener {
            // オーバーロードがたくさんあるのでお好みで
            // val action = StartFragmentDirections.actionStartFragmentToFirstFragment()
            // findNavController().navigate(action)

            // Shared elementsが必要な場合は
            val extras = FragmentNavigatorExtras(
                view to "imageView"  // Pair<View, String>
            )
            val action = StartFragmentDirections.actionStartFragmentToFirstFragment()
            findNavController().navigate(action, extras)
        }
        binding.button_first2.setOnClickListener {
            // 引数はNavDirectionsの引数として詰める
            val action = StartFragmentDirections.actionStartFragmentToSecondFragment(1)
            findNavController().navigate(action)
        }
        binding.buttonfirst3.setOnClickListener {
            // DialogFragmentも他と変わらず操作できる
            val action = StartFragmentDirections.actionStartFragmentToDialogFragment()
            findNavController().navigation(action)
        }
    }

    override fun onDestroyView() {
        super.onDestroyView()

        // bindingはここでリリース
        _binding = null
    }
}
```
``` kotlin:FirstFragment.kt
class FirstFragment: Fragment() {
    private var _binding: FirstFragmentBinding? = null
    private val binding get() = _binding!!

    // ...略

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        // shared transition
        val sharedTransition = TransitionInflater.from(context)
            .inflateTransition(R.transition.image_transition)
        sharedElementEnterTransition = sharedTransition
        sharedElementReturnTransition = sharedTransition

        return binding.root
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        binding.button_first.onClick.setOnClickListener{
            val action = FirstFragmentDirections.actionFirstFragmentToStartFragment()
            findNavController().navigate(action)
        }
    }

    // ...略
}
```
``` kotlin:SecondFragment.kt
class SecondFragment: Fragment() {
    private var _binding: SecondFragmentBinding? = null
    private val binding get() = _binding!!
    private val args: SecondFragmentArgs by navArgs()

    // ...略

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        // shared transition
        val sharedTransition = TransitionInflater.from(context)
            .inflateTransition(R.transition.image_transition)
        sharedElementEnterTransition = sharedTransition
        sharedElementReturnTransition = sharedTransition

        return binding.root
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        val num = args.num
        Log.d("SecondFragment", "num: $num")

        binding.button_second.onClick.setOnClickListener{
            val action = SecondFragmentDirections.actionSecondFragmentToStartFragment()
            findNavController().navigate(action)
        }
    }

    // ...略
}
```
DialogFragmentは変更せずに実装可
#### 注入するmain_activityのレイアウト
``` xml:MainActivity.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <androidx.fragment.app.FragmentContainerView
        android:id="@+id/fragmentContainerView"
        android:name="androidx.navigation.fragment.NavHostFragment"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:defaultNavHost="true"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:navGraph="@navigation/nav_graph" />
</androidx.constraintlayout.widget.ConstraintLayout>
```

# 実装は正しいのに解決できない場合
だいたいの`Unresolved reference`は`Clean Project`すれば治ります。
[**Build**] > [**Clean Project**]から完了後、[**Build**] > [**Rebuild Project**]すればおおよそ治ります。
これで治らなけばバグか実装方法が間違えてます。

# 所感
iOSのストーリーボードみたいに直感的に開発できるので便利！　ただ、まだ発展途上って感じが否めない気がする。
CodeよりもDesignタブから書くほうが便利なんだけど、dioalogとかは手打ちじゃないといけないところとか、ドキュメントが少ないところとか（まぁ公式で割と事足りる部分もあるけど）。
新しいアプリを作るときにはデフォルトでNavigationコンポーネントを使用してるので今後の発展に期待したいところですが、[Jetpack Compose](https://developer.android.com/jetpack/compose?hl=ja)なるものも出てきてどれを勉強していけばいいのか悩みものですが、Navigatioコンポーネントは今までのコードほぼ崩さずに実装できるので、そこはよかったかなと。

## 以前書いたものが気にしなくてもよくなった
Navigationコンポーネントに全部移譲しているので現在のFragmentが何かとか判定しなくてもよくなったので以下の記事が、まるまる無駄になりました。ありがたやー。
https://qiita.com/blue928sky/items/777f527ef203d0f5c3d5

# 参考
- [ナビゲーション &nbsp;|&nbsp; Android デベロッパー &nbsp;|&nbsp; Android Developers](https://developer.android.com/guide/navigation?hl=ja)
- [Jetpack Compose &nbsp;|&nbsp; Android Developers](https://developer.android.com/jetpack/compose?hl=ja)