I have an observer than when it's called changes fragments.
The problem is when i go back the observer is called immediately and my app crashes with a
java.lang.IllegalArgumentException: navigation destination com.superapps.ricardo.tablepro:id/action_searchFragment_to_yourGameList2 is unknown to this NavController.
I don't understand why it is being called.
this is the only method that changes the list
override fun onSuccess(gamePair: Pair<Int, List<BggGame>>) {
CoroutineScope(Main).launch{
//goToList(gamePair.second, binding.input.text.toString())
viewModel.setGameList(gamePair.second)
}
}
and this is the viewmodel creation and change fragment code
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
viewModel = ViewModelProviders.of(this).get(SearchViewModel::class.java)
viewModel.gameList.observe(viewLifecycleOwner, Observer {
goToList(it, binding.input.text.toString())
})
}
private fun goToList(games: List<BggGame>, user: String) {
val action = SearchFragmentDirections.actionSearchFragmentToYourGameList2(user)
val gameList = GameList()
gameList.gameList = games
action.gameList = gameList
try {
Navigation.findNavController(view!!).navigate(action)
viewModel.gameList.removeObservers(viewLifecycleOwner)
} catch (e: Exception){
Log.e("a0,","a..", e)
}
progressDialog.dismiss()
}
LiveData
keeps the last value that have been set. When calling observe()
on a LivaData
, if the LiveData
has a value, the observer is instantly called with the value previously set.
If you want to use LiveData
for "events" like your usecase, your live data should expose an Event
object that can be consumed only once.
Here is an example of a good implementation of such an Event
class.
From the article :
open class Event<out T>(private val content: T) {
var hasBeenHandled = false
private set // Allow external read but not write
/**
* Returns the content and prevents its use again.
*/
fun getContentIfNotHandled(): T? {
return if (hasBeenHandled) {
null
} else {
hasBeenHandled = true
content
}
}
/**
* Returns the content, even if it's already been handled.
*/
fun peekContent(): T = content
}
Usage in ViewModel :
val gameList = MutableLiveData<Event<List<BggGame>>()
fun setGameList(gameList: List<BggGame>) {
gameList.value = Event(gameList)
}
Usage in view :
viewModel.gameList.observe(this, Observer {
it.getContentIfNotHandled()?.let { // Only proceed if the event has never been handled
goToList(it, binding.input.text.toString())
}
})