見出し画像

parentFragmentのスコープで生成するparentViewModels()

viewModels()とactivityViewModels()

Android ViewModelの生成は、activity-ktx, fragment-ktxで今はこう書くことができます。

val viewModel: MainViewModel by viewModels()

こう書くことでお手軽にViewModelを生成、そして管理することができるようになりました。まだ未生成の場合は生成して、画面回転やBackキーなどで戻ってきたときはViewModelProvider経由で取得してくれます。

例えばMainFragmentでviewModels()で生成したときは、このFragmentだけで有効なViewModelになります。

class MainFragment : Fragment() {
    val mainViewModel: MainViewModel by viewModels()
}

class SubFragment : Fragment() {
    // MainFragmentで生成したmainViewModelとは共有されてない
    val mainViewModel: MainViewModel by viewModels()
}

ダイアログやBottomNavigation、ViewPager2などを使っていると、親のFragmentのViewModelを子のFragmentで共有して欲しい時があります。

その時のためにactivityViewModels()があります。

class MainFragment : Fragment() {
    val mainViewModel: MainViewModel by activityViewModels()
}

class SubFragment : Fragment() {
    // 同じActivityならシェアされる!
    val mainViewModel: MainViewModel by activityViewModels()
}

これはViewModelの生存スコープがActivityになるので、同じActivity上のFragmentであればViewModelがシェアされるようになります。

activityViewModels()でシェアすることが問題になるケース

基本的にはこのactivityViewModels()でいい気がします。ですが、シングルActivityなどで実装されているプロジェクトの場合はスコープの範囲が広すぎてしまい、思わぬバグを引き起こしてしまいます。

例えば、いろいろな画面で使われるDialogのViewModelをシェアする時などです。このDialogのViewModelを呼び出しもとのFragmentで扱うためにactivityViewModels()を使うと、他の画面でこのDialogを呼び出した時に値が再発火してしまったりするからです。

そこで、呼び出しもとの親Fragmentと、呼び出されるDialogなどの子Fragmentと言うスコープ間でシェアされるviewModels()が必要になってきます。

parentViewModels()

parentViewModels()はデフォルトでは用意されてませんが、viewModels()のコードをちょっといじってparentFragmentで管理してくれる拡張を書いてみました。

このコードを適当にExtentions.ktなどに書いておきます。あとは通常のviewModels()と同じように使ってあげます。

MainFragmentで生成したViewModelをSubFragmentでも使いたい場合は下記のような感じで使います。

class MainFragment : Fragment() {
    val mainViewModel: MainViewModel by viewModels()
}

class SubFragment : Fragment() {
    // parentFragmentのスコープからMainViewModelを取得する
    val mainViewModel: MainViewModel by parentViewModels()
}


この記事が気に入ったらサポートをしてみませんか?