I have a scenario, where two fragments (A and B) need to share a list of objects, which can be mutated from both screens. A and B fragments have a corresponding ViewModel each (A_ViewModel and B_ViewModel). They contain some logic on how the shared data is mutated.
To keep the shared data in tact, I have created AB_Flow_ViewModel, which is activity scoped and contains that shared data wrapped in a MutableLiveData
.
I have decided to pass that MutableLiveData
object to A and B child fragments as a constructor parameter.
I have never seen such approach anywhere, but it seems to work perfectly for me.
Are there any drawbacks of doing this?
This is the pseudo code for my solution:
// Navigation: (A -> B)
class A() : Fragment() {
private val viewModel: A_ViewModel ...
override fun onViewCreated(
view: View,
savedInstanceState: Bundle?
) {
super.onViewCreated(view, savedInstanceState)
viewModel.sharedData.observe {
...
}
}
}
class A_ViewModel @Inject constructor(
val sharedData: MutableLiveData<List<Any>>
) : ViewModel() {
fun changeSharedData(newSharedData: List<Any>) {
sharedData.value = newSharedData
}
}
@Module
class A_Module {
@Provides
@FragmentScoped
fun provideSharedData(fragment: A) =
fragment.activityViewModels<AB_Flow_ViewModel>().value.sharedData
}
class B() : Fragment() {
private val viewModel: A_ViewModel ...
override fun onViewCreated(
view: View,
savedInstanceState: Bundle?
) {
super.onViewCreated(view, savedInstanceState)
viewModel.sharedData.observe {
...
}
}
}
@Module
class B_Module {
@Provides
@FragmentScoped
fun provideSharedData(fragment: B) =
fragment.activityViewModels<AB_Flow_ViewModel>().value.sharedData
}
class B_ViewModel @Inject constructor(
val sharedData: MutableLiveData<List<Any>>
) : ViewModel() {
fun changeSharedData(newSharedData: List<Any>) {
sharedData.value = newSharedData
}
}
class AB_Flow_ViewModel() : ViewModel() {
val sharedData = MutableLiveData<List<Any>>()
}
I strongly suggest not to share MutableLiveData between two fragments, if you get a bug it will get super hard to debug because you won't know who is changing that data and where to cause this weird state. What I like to do is keep all my MutableLiveData inside the ViewModel and expose only LiveData to the Fragments/View to observe. Every change that needs to happen to the MutableLiveData I do it through a function inside the ViewModel.
private val _sharedData = MutableLiveData<List<Any>>()
val sharedData: LiveData<List<Any>>
get() = _sharedData
I would put fun changeSharedData inside AB_Flow_ViewModel and place all the logic in one ViewModel instead of two. If the logic needs to be different for FragA and FragB I would use two different methods that will change the same LiveDdata