JetpackComposeのViewModelのスコープ、共通のViewModelの取得方法

# 初めに
`JetpackCompose`で`viewModel()`を呼び出して`ViewModel`を取得する場合、`CompositionLocal`経由で取得した`ViewModelStoreOwner`が`ViewModel`のスコープになります。標準で`ViewModelStoreOwner`を実装しているクラスには`ComponentActivity`、`Fragment`、`NavBackStackEntry`があり、呼び出し階層で一番近い物が適用されます。現在のAndroidアプリの作りでは、`NavBackStackEntry`をスコープとした`ViewModel`を取得して使用することが多いと思います。また、`viewModel()`の引数で`ViewModelStoreOwner`を指定することで、特定のスコープの`ViewModel`を取得することもできます。`Hilt`を使ってDIする場合でも`viewModel()`を`hiltViewModel()`に置き換えるだけで使い方は変わりません。

# ComponentActivity、Fragment
ナビゲーショングラフのデスティネーション内から、`ComponentActivity`や`Fragment`をスコープとした`ViewModel`を取得する方法です。`ComponentActivity`や`Fragment`といった大きなスコープの`ViewModel`は、アプリ全体を通して共有したい値を保持する場合などに使用されます。
```kotlin:MainContent.kt
@Composable
fun MainContent(
    modifier: Modifier = Modifier
) {
    val navController = rememberNavController()
    NavHost(
        modifier = modifier,
        navController = navController,
        startDestination = MenuPageRoute
    ) {
        composable<MenuPageRoute> {
            // Fragmentを使用しておらず、ActivityスコープのViewModelを取得する場合
            val viewModel = viewModel<MainActivityViewModel>(
                LocalContext.current as ComponentActivity)
            // Fragmentを使用しており、ActivityスコープのViewModelを取得する場合
            val viewModel = viewModel<MainActivityViewModel>(
                (LocalContext.current as Fragment).requireActivity())
            // Fragmentを使用しており、FragmentスコープのViewModelを取得する場合
            val viewModel = viewModel<MainActivityViewModel>(
                LocalContext.current as Fragment)
            MenuPage(
                navController
            )
        }
    }
}
```



# 異なるナビゲーショングラフのデスティネーション
ナビゲーショングラフのデスティネーション内から、異なるデスティネーションをスコープとしたViewModelを取得する方法です。ユーザー登録画面など、複数のデスティネーション間で共通のViewModelを参照したい場合などに使用されます。
```diff_kotlin:MainContent.kt
@Composable
fun MainContent(
    modifier: Modifier = Modifier
) {
    val navController = rememberNavController()
    NavHost(
        modifier = modifier,
        navController = navController,
        startDestination = MenuPageRoute
    ) {
        composable<MenuPageRoute> {
            val viewModel = viewModel<MainActivityViewModel>(
            MenuPage(
                navController
            )
        }
+        composable<ScreenARoute> { backStackEntry ->
+            // ScreenARouteスコープのViewModelを取得
+            val viewModel = viewModel<ScreenPageViewModel>()
+            ScreenAPage(
+                navController
+            )
+        }
+        composable<ScreenBRoute>{ backStackEntry ->
+            // ScreenARouteのBackStackEntryを取得
+            val parentBackStackEntry = remember(backStackEntry) {
+                navController.getBackStackEntry(ScreenARoute)
+            }
+            // ScreenARouteスコープのViewModelを取得
+            val viewModel = viewModel<ScreenPageViewModel>(parentBackStackEntry)
+            ScreenBPage(
+                navController
+            )
+        }        
    }
}
```
公式サイトには上記の様に、異なるデスティネーションの`route`を直接指定して`BackStackEntry`を取得する例が記載されていますが、`ScreenARoute`を呼び出す前に`ScreenARoute`を呼び出すとエラーが発生するため、下記の様に、ナビゲーショングラフをネストさせて利用した方が安全です。下記の例では、`ScreenCRoute`、`ScreenBRoute`、どちらかの`BackStackEntry`が存在していれば、`ScreenParentRoute`の`BackStackEntry`も存在するため、共通の`ViewModel`も存在することになります。
```diff_kotlin:MainContent.kt
@Composable
fun MainContent(
    modifier: Modifier = Modifier
) {
    val navController = rememberNavController()
    NavHost(
        modifier = modifier,
        navController = navController,
        startDestination = MenuPageRoute
    ) {
        composable<MenuPageRoute> {
            val viewModel = viewModel<MainActivityViewModel>(
            MenuPage(
                navController
            )
        }
+        navigation<ScreenParentRoute>(
+            startDestination = ScreenCRoute,
+        ) {
+            composable<ScreenCRoute> { backStackEntry ->
+                // ScreenParentRouteのBackStackEntryを取得
+                val parentBackStackEntry = remember(backStackEntry) {
+                    navController.getBackStackEntry(ScreenParentRoute)
+                }
+                // ScreenParentRouteスコープのViewModelを取得
+                val viewModel = viewModel<ScreenPageViewModel>(parentBackStackEntry)
+                ScreenCPage(
+                    navController
+                )
+            }
+            composable<ScreenDRoute>{ backStackEntry ->
+                // ScreenParentRouteのBackStackEntryを取得
+                val parentBackStackEntry = remember(backStackEntry) {
+                    navController.getBackStackEntry(ScreenParentRoute)
+                }
+                // ScreenParentRouteスコープのViewModelを取得
+                val viewModel = viewModel<ScreenPageViewModel>(parentBackStackEntry)
+                ScreenDPage(
+                    navController
+                )
+            }
+        }
    }
}
```
# 参考
[ViewModel スコープ用 API &nbsp;|&nbsp; Android Developers](https://developer.android.com/topic/libraries/architecture/viewmodel/viewmodel-apis?hl=ja)