MVVMパターンにおいてFragment間でデータを共有するには、ViewModelを使うのがよいとされています。ViewModelのインスタンスを複数のFragmentに挿入するには、KoinのsharedViewModel()
を使うと便利です。
しかし、普通にsharedViewModel()
を使うと、ViewModelOwnerはActivityになります。これで問題がなければ構わないのですが、Navigation ComponentのSingle Activityアーキテクチャを使っている場合など、各FragmentのViewModelがすべて一つのActivityに紐づいてしまうと困る場合もあります。
そこで、KoinのsharedViewModel()
で、特定のFragmentをOwnerに指定しつつ別のFragmentにも同じインスタンスを挿入する方法を紹介します。
(注)Koinのバージョンは2.2.2で確認しています。比較的最近追加された機能ですので、古いバージョンでは動作しない可能性があります。
import org.koin.android.viewmodel.ViewModelOwner.Companion.from
import org.koin.android.viewmodel.ext.android.sharedViewModel
// 親フラグメント
class MainFragment: Fragment() {
private val viewModel: SampleViewModel by sharedViewModel(owner = { from(this) })
}
// 子フラグメント
class SubFragment: Fragment() {
private val viewModel: SampleViewModel by sharedViewModel(owner = { from(requireParentFragment()) })
}
SubFragment
はMainFragment
の子供として配置されているものとします。MainFragment
ではthis
を、SubFragment
ではrequireParentFragment()
をsharedViewModel()
のowner
引数に与えることで、MainFragment
をOwnerとする共通のViewModelインスタンスを挿入することができます。
これで、MainFragment
が作成されるたびにSampleViewModel
のインスタンスが作成され、なおかつSubFragment
からはMainFragment
と同じSampleViewModel
のインスタンスにアクセスすることができます。
module宣言は通常通りviewModel
を宣言すればOKです。
@JvmField
val appModlue = module {
viewModel { SampleViewModel() }
}
moduleの宣言には特に手を加えず、挿入する側で挿入方法を柔軟に設定できるあたり、Koinは本当によくできてるなと感じます。公式の説明は下記にあります。もう少し具体的に書いてくれていると助かるんですけどね・・・。ってあれ?サイトのデザインがきれいになってる!