onActivityResult() for Fragments!
Since the launch of Android’s jetpack navigation component, it has removed a lot of boilerplate code which we used to write earlier. Passing data between fragments has become a lot easier using safeArgs and bundles. Let’s revisit how we send data from Fragment A to Fragment B using a navController
.
The above code snippet shows how we can pass the data from Fragment A to Fragment B. But what if we want to send some data back to Fragment A? We can’t pass a bundle when using navigateUp().
One thing we can do is use an activityViewModels()
which will be shared between fragments. A variable change in an activityViewModels()
will reflect in all the fragments associated with that viewModel(). But it’s generally a bad practice as it’ll make things complex in case of multiple fragments and the lifecycle of that viewModel will persist as long as the activity is alive. It’d also make code debugging very confusing. So what’s the other approach?
Solution
Let’s explain this using a simple app that consists of 2 fragments. FragmentA
shows the result and FragmentB
does the calculation. I’ll leave the Github link to this app in case you want to refer UI related stuff. Here I'll show the logic of passing data from FragmentB
to FragmentA
.
To pass the data while also popping that fragment from the stack, we use the so-called savedStateHandle
.
According to Android developers,
SavedStateHandle
class is a key-value map that allows you to write and retrieve data to and from the saved state throughset()
andget()
methods. Additionally, you can retrieve values from SavedStateHandle that are wrapped in a LiveData observable usinggetLiveData()
. When the key's value is updated, the LiveData receives the new value.
In short, it works just like a bundle does; except it can be used to save the current state and pass the values from one fragment to another. In the below code when you’re popping FragmentB, you can set a savedStateHandle just like you instantiate a bundle using a key-value pair. Key is any sort of string you can define your companion object and value can be of any data type or parcelable in case of objects, here we are passing a simple integer value.
Avoid using serializable values as Serialization can consume lots of memory if the objects being serialized are complicated.
You’ve successfully passed the data from FragmentB. Now to get this value in your FragmentA, you can use the same key you used in FragmentB like so.
Keep in mind to use currentBackStackEntry’s savedStateHandle when in Fragment A and previousBackStackEntry’s handle when in FragmentB
SavedStateHandle also survives configuration change. So even if you rotate your phone or switch from a multi-window to full screen, onViewCreated()
will be called after onDestroyView()
, but your value will be preserved. However, if you wish to clear the values contained by the handle, you can do so by writing
handle.remove<Int>(<your key>)
This will clear the handle’s value associated with that particular key. And that’s pretty much it! Now you can easily get back data from any fragment while popping up from the stack.
You can check out my GitHub repo here for full source code. Any doubts or suggestions in the comment section are most welcome. Also, do you know you can clap more than once in Medium? 😉