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.
Navigation should always be handled in the ViewModel. You're on the right track with thinking that the perfect implementation of the MVVM design pattern would mean you could run your application entirely without Views, and you can't do that if your Views control your Navigation.
Note: You can combine Clean Architecture with the model-view-presenter (MVP) architecture as well. But since Android Architecture Components already provides a built-in ViewModel class, we are going with MVVM over MVP—no MVVM framework required!
MVVM stands for Model, View, ViewModel. Model: This holds the data of the application. It cannot directly talk to the View. Generally, it's recommended to expose the data to the ViewModel through Observables.
NavController manages app navigation within a NavHost . Apps will generally obtain a controller directly from a host, or by using one of the utility methods on the Navigation class rather than create a controller directly. Navigation flows and destinations are determined by the navigation graph owned by the controller.
Here is an example of a single activity User-Login android application to show the implementation of the MVVM architecture pattern on projects. The application will ask the user to input the Email ID and password. Based on the inputs received the ViewModel notifies the View what to show as a toast message.
Model — View — ViewModel (MVVM) is the industry-recognized software architecture pattern that overcomes all drawbacks of MVP and MVC design patterns. MVVM suggests separating the data presentation logic (Views or UI) from the core business logic part of the application. The separate code layers of MVVM are:
In this article, I will be talking about navigation. Nowadays in Android development, the best practice is to use single activity and multiple fragments. Using single activity and multiple fragments introduces a process that needs to be handled. That process is navigation between fragments.
The navigation host is an empty container where destinations are swapped in and out as a user navigates through your app. A navigation host must derive from NavHost. The Navigation component's default NavHost implementation, NavHostFragment , handles swapping fragment destinations.
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.
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)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With