I'm very new to Kotlin Flows. As the title suggests I basically have 2 Fragments that Shares a ViewModel. I want to send data between them using SharedFlow as a substitute for LiveData without retaining it's state.
Fragment A
class FragmentA: Fragment() {
private lateinit var viewModelShared: SharedViewModel
//Others//
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewModelShared = ViewModelProvider(requireActivity())[SharedViewModel::class.java]
someView.setOnClickListener{
viewModelShared.sendData("Hello")}
//Fragment Navigates From Fragment A to B using NavController
navController.navigate(some_action_id)
}
}
Fragment B
class FragmentB: Fragment() {
private lateinit var viewModelShared: SharedViewModel
//Others//
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewModelShared = ViewModelProvider(requireActivity())[SharedViewModel::class.java]
lifecycleScope.launchWhenCreated {
viewModelMain.sharedFlow.collectLatest {
//Data or the word 'Hello' sent from Fragment A not being Received Here
}
}
}
SharedViewModel
class SharedViewModel:ViewModel() {
private val _sharedFlow= MutableSharedFlow<String>()
val sharedFlow= _sharedFlow.asSharedFlow()
fun sendData(data:String){
viewModelScope.launch {
_sharedFlow.emit(data)
}
}
}
CodePudding user response:
Your FragmentB only collects when it is in started state or higher. This is correct because you don't want to be working with views when it's not visible and possibly doesn't currently have views.
However, since your SharedFlow has no replay history, this means there is nothing for FragmentB to collect when it is back on screen. Presumably it is off screen when FragmentA updates the flow.
So, your SharedFlow with no replay has nothing to do when it currently has no collectors, and the emitted value is thrown away. You need to have a replay of at least 1 for this to work.
private val _sharedFlow= MutableSharedFlow<String>(replay = 1)
You mentioned "without retaining its state", but this isn't possible without state if the two fragments are not on screen at the same time.
By the way, there is a simpler way to declare your shared ViewModel:
private val viewModelShared: SharedViewModel by activityViewModels()