Android provides a very good Data Binding library to bind the layout views to a ViewModel. When I initially started out Android development there wasn't the Data Binding library and most of the view changes were made from Fragment.
This quickly bloated the Fragment file and it become hard to manage over time. Luckily with the introduction of Jetpack and the Data Binding library, the code maintenance and management become very easy along with the use of Kotlin language. All the business logic was now moved to ViewModel and the view interactions are easier to manage without having to write additional code in the fragment.
This worked pretty good for most of the scenarios I was dealing with in my app. But I was still relying on using callbacks to handle navigation, until recently. I will discuss both of these approaches below.
Consider the scenario where we want to update information on a fragment. We want the fragment to navigate to another fragment on successful update operation. We have a Menu item in our fragment which would invoke the method on ViewModel and send the callback to notify the fragment on successful completion.
While this approach worked fine initially for me, after a certain time it becomes harder to manage if your ViewModel has nested callbacks and the error handling becomes complicated. Handling this with coroutines is much more cleaner.
Using Coroutines with LiveData Event Observer
We will be looking at a similar example but this time it will be on record deletion. Consider two Fragments A and B. Fragment B is the child fragment which has a photo we want to delete using a button. When the photo is deleted the user should be navigated back to Fragment A. Here is how it works
- User clicks a button on Fragment B
- ViewModel method is than invoked but this time we do not pass a callback
- We use a wrapper observer on a LiveData event in our ViewModel. I'm using the code from the Android Architecture Sample
- Here is how we initialize the LiveData event class
- When the ViewModel completes the delete event it notifies any event observers about the successful deletion
- The Fragment B from which the button click event has triggered is setup to observe any events on the photoDeletedEvent. When the delete was successful it successfully calls the Navigate action.
There are a couple of advantages to using this approach
- The code uses a top down approach flow which is easier to follow compared to callbacks
- The above example uses the click listener in the fragment but if you are directly binding the click event to ViewModel something like below, the code becomes even cleaner and we would not need additional code in our Fragment.
- Since the ViewModel click method is decoupled from the Fragment and we are not sending the callback function, unit testing the ViewModel methods becomes easier to manage.
I personally prefer this approach and would leverage this and optimize it further in my Android projects. Let me know your thoughts on this approach in the comments section below and if you have any better ways to manage navigation from ViewModel.
Have a great day!