0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

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

Posted at

初めに

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

ComponentActivity、Fragment

ナビゲーショングラフのデスティネーション内から、ComponentActivityFragmentをスコープとしたViewModelを取得する方法です。ComponentActivityFragmentといった大きなスコープのViewModelは、アプリ全体を通して共有したい値を保持する場合などに使用されます。

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を参照したい場合などに使用されます。

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を呼び出すとエラーが発生するため、下記の様に、ナビゲーショングラフをネストさせて利用した方が安全です。下記の例では、ScreenCRouteScreenBRoute、どちらかのBackStackEntryが存在していれば、ScreenParentRouteBackStackEntryも存在するため、共通のViewModelも存在することになります。

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  |  Android Developers

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?