So I'm trying to implement mvvm architecture with navigation component, this code snipet works just fine.. the problem is that when I navigate from fragmentA to FragmentB there is no way to go back to FragmentA, its like the button is blocked (If I remove the ViewModel the navigation works fine so it has something to do with the ViewModel).
anyone have an idea of whats happening?
private fun initVewModel() {
myViewModel = MyViewModel()
myViewModel = ViewModelProviders
.of(this, createFor(myViewModel))
.get(MyViewModel::class.java)
}
private fun setupObservers() {
myViewModel.myData.observe(viewLifecycleOwner, Observer {
it?.let { resource ->
when (resource.status) {
Status.SUCCESS -> {
resource.data?.let {
val action =
FragmentADirections.actionFragmentAToFragmentB()
Navigation.findNavController(requireView()).navigate(action)
}
}
Status.ERROR -> {
//TODO show an error
loading.visibility = View.GONE
}
Status.LOADING -> {
loading.visibility = View.VISIBLE
}
}
}
})
}
This is my ViewModel
class MyViewModel : ViewModel() {
private val searchText = MutableLiveData<String>()
//This method is called on a search button click
fun setSearchText(text: String) {
searchText.value = text
}
val myData = searchText.switchMap { text ->
liveData(Dispatchers.IO) {
emit(Resource.loading(data = null))
try {
emit(Resource.success(data = dataOrigin.getData(text)))
} catch (exception: Exception) {
exception.printStackTrace()
emit(Resource.error(data = null, message = exception.message ?: "Error Occurred!"))
}
}
}
}
That's because your myData
Observer
will be immediately triggered with the latest data on back navigation and trigger the SUCCESS
branch once again.
I recommend either using SharedFlow
s or checking out a SingleLiveEvent
pattern for LiveData
, where you can only consume an event once.
UPDATE:
A possible solution for the consumable pattern:
class Consumable<T>(private val event: T) {
private var isConsumed = false
fun consume(): T? {
if (isConsumed) return null
isConsumed = true
return event
}
fun peek() = event
}
Then in the ViewModel :
val event = MutableLiveData<Consumable<YourEventClass>>()
...
event.value = Consumable(XYEvent)
And you'd observe it like this:
viewModel.event.observe{ event ->
event.consume()?.let{
// Do your navigation here
}
}
Additionally you could write an extension function .consume()
, for consumable livedatas, which unwraps the value
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.