スポンサーリンク

Jetpack Composeでnavigate時のパラメータ受け渡し

スポンサーリンク

何個か方法がありそうですね.Android公式をみてもいまいちどれを使えば分からなかったのでまとめておきます

まず前提ですが,navigate時に授受する値は必要最低限のものにすべきで,多くの要素を共有する必要がある場合には,そのdestination間で共通して使用するviewModelを定義し,そこで管理すべきです.

1.savedStateHandleを利用する

これはroute情報のパラメータは自動的にsavedStateHandleにkey-valueとして登録されることを利用して,viewModelのsavedStateHandleから取得する方法です.

//NavHost
composable(Screen.Screen1.name){
      Screen1(
          onClick = {name ->
              navController.navigate(Screen.Screen2.name + "/${name}/test11/test22")
          }
      )
}
composable(Screen.Screen2.name + "/{name}/{test1}/{test2}"){
      Screen2()
}

//ViewModel
@HiltViewModel
class Screen2ViewModel @Inject constructor(
    savedStateHandle: SavedStateHandle
) : ViewModel() {

    var name: String = ""

    init{
        savedStateHandle.get<String>("name")?.let {
            name = it
        }
    }
}

routeの第二パラメータ以降は全て,そのdestinationで使用されるviewModelのsavedStateHandleに登録されます.上記ではnameしか表示していませんが,

  • name = 受け渡しされたname
  • test1 = test11
  • test2 = test22

として登録されています.viewModelに直接値を渡したい場合はこの手法で良さそうです

2.composableのargumentとして受け取る

これは公式サイトに記載されている手法です.呼び出し元は同じなので,呼び出し先のcomposableだけ記載します.

    composable(
        Screen.Screen2.name + "/{name}/{test1}/{test2}",
        arguments = listOf(
            navArgument("name") { type = NavType.StringType },
            navArgument("test1") { type = NavType.StringType },
            navArgument("test2") { type = NavType.StringType },
        )
    ) {backStackEntry ->
        backStackEntry.arguments?.apply {
            SuggestionScreen(get("name"), get("test1"), get("test2"))
        }
    }

routeの第二パラメータ以降は全て navArgumentとして取得可能で,backStackEntry内にbundleとして保存されます.使用する際はbackStackEntryから取得する必要があります.これはviewModelへの伝播が必要ない,StatelessなScreenに値を渡したい場合に使えそうです.

Navigation with Compose  |  Jetpack Compose  |  Android Developers

3.値で初期化したviewModelを渡す

Hiltを利用している場合,以下のように先にviewModelをhiltから取得し,それをセットすることで初期化されたviewModelを与えられます.

composable(
    Screen.Screen2.name + "/{name}/{test1}/{test2}",
    arguments = listOf(
        navArgument("name") { type = NavType.StringType },
        navArgument("test1") { type = NavType.StringType },
        navArgument("test2") { type = NavType.StringType },
    ),
    content = {backStackEntry ->
        val viewModel = hiltViewModel<Screen2ViewModel>()
        backStackEntry.arguments?.apply {
            viewModel.name = get("name").toString()
            Screen2(viewModel)
        }
    }
)

ただこの手法の場合,NavHostがviewModelの操作をしてしまうので,気持ちが悪いですね.

どれを使う

viewModelに値を入れたいなら1, statelessなcomposeを構成するだけなら2でいいと思います.3は依存が複雑になるのでテストしづらい状況になります.

タイトルとURLをコピーしました