I have an app using Androids ViewModel class and Navigation Component for navigating between fragments. How would I handle navigation from the ViewModel? I am using RxJava and I was thinking of having the Fragments listen for navigation events and then trigger navigation that way. What is the normal way to handle this? I am also using Dagger for dependency injection if that helps.
3 Answers
Per the LiveData with SnackBar, Navigation, and other events blog post:
Some data should be consumed only once, like a Snackbar message, a navigation event or a dialog trigger.
Instead of trying to solve this with libraries or extensions to the Architecture Components, it should be faced as a design problem. We recommend you treat your events as part of your state.
They detail the use of a SingleLiveEvent class which ensures that each Navigation event is only received once by an Observer (i.e., your Fragment that has access to your NavController
).
The other alternative is to use an 'Event wrapper' model where the event must be explicitly marked as handled.

- 1
- 1

- 191,609
- 30
- 470
- 443
-
13Although I agree this is the official recommendation, I think Google made a big mistake here. Navigation is not part of a particular viewmodel's "state" nor is it an event. It's its own entity (I like to think of it as a service) which may contain state and events of it's own. The fact that they need to invent a SingleLiveEvent just for this is proof of this. – Glaucus Oct 02 '18 at 17:59
-
If I have many possible destination from the current Screen. Should I have 1 `SingleLiveEvent` or `LiveData` with `Event` Wrapper for each possible destination? What's the best practice for this? – Archie G. Quiñones Feb 21 '19 at 06:00
-
3https://medium.com/google-developer-experts/using-navigation-architecture-component-in-a-large-banking-app-ac84936a42c2 has a clean implementation of posting navigation events from the ViewModel – amitavk Apr 24 '19 at 06:30
-
Isn't SingleLiveData an anti-pattern? – Cassio Landim Jul 02 '20 at 19:28
If you were using MVP, P would just call the method on V that triggers navigation.
The equivalent approach in MVVM would be to have a dedicated observable/listener/callback, if done with RxJava it could be driven by a PublishSubject
. This satisfies the one-time requirement. If instead you need to respond to events that may be emitted before you subscribed, you can use a BehaviorSubject<Optional<T>>
and create it with createDefault(absent<T>())
, trigge it with onNext(Optional.of(navigationObject))
and then let the VM know when navigation occurs, and then the VM can clear it with onNext(absent())
Alternatively if you want to work it into some all-encompassing redux/mvi-like state, you might have some state class with all the state including some property that indicates to the view to navigate somewhere, upon receiving/acting on this the View would tell the VM it has done so and the VM would set the state to the same as current but without navigation. e.g. (in Kotlin) state = state.copy(navigateToX = false)

- 2,596
- 2
- 19
- 26
for your events, in general, you could use something like this:
/**
* Used as a wrapper for data that is exposed via a LiveData that represents an event.
*/
open class Event<out T>(private val content: T) {
@Suppress("MemberVisibilityCanBePrivate")
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
}
with this you can easily make sure you have used the content once like this:
viewModel.getShowAlertFragment().observe(viewLifecycleOwner, Event{
event?.getContentIfNotHandled()?.let {
// your code here
}
})
but to make it even simpler and actually avoid checking if the content has been used before everywhere you can create another class that extend the observer and do the cheking there:
/**
* An [Observer] for [Event]s, simplifying the pattern of checking if the [Event]'s content has
* already been handled.
*
* [onEventUnhandledContent] is *only* called if the [Event]'s contents has not been handled.
*/
class EventObserver<T>(private val onEventUnhandledContent: (T) -> Unit) : Observer<Event<T>> {
override fun onChanged(event: Event<T>?) {
event?.getContentIfNotHandled()?.let {
onEventUnhandledContent(it)
}
}
}
now with this, you can simplify your code to something like this:
viewModel.getShowAlertFragment().observe(viewLifecycleOwner, EventObserver {
// do you stuff here and you know it will only be called once
}
})
The idea was taken from this file in Android samples Here

- 3,646
- 1
- 37
- 38