As an Android developer, we all should be familiar with the startActivityForResult function. We use this function for starting a new Activity and getting results back to current the Fragment or Activity. However, when the screens in my application are represented by Fragments, and I can't use startActivityForResult for passing results between them.
Until recently, when it comes to the communication between Fragments there is nothing similar to startActivityForResult. Therefore, I had to create shared ViewModel between Fragments so that I can pass data back and forth between them.
Fortunately, Google has just added a new ability to the Fragment family. There are two approaches to pass data back and forth between Fragments; first is by using FragmentManager and second is by using the Navigation component library.
Then let's see how we can use this new ability in action. I will build a simple note list application with two Fragments with the Navigation component library. One fragment for displaying the list of notes and another one for taking note.
Let's start by adding important dependencies. By the time this article is written the latest Fragment version is 1.3.0-alpha05.
The new ability added to FragmentManager made the FragmentManager be able to act as a central store for Fragment results. Although the function is not named startFragmentForResult, it acts similarly to startActivityForResult. With the snippet below, we can pass the data back and forth between Fragments easily. This ability has been added since Fragment 1.3.0-alpha04.
For NoteListFragment to get the result back from AddNoteFragment. We have to set a result listener. Let's take a look at the NoteListFragment.
As you can see, there is an extension function called setFragmentResultListener provided. We get the result back as a bundle.
Moving on to the AddNoteFragment. We need to set result, and it can be done easily as follow.
private fun navigateBack() {
setFragmentResult(
NoteListFragment.ADD_NOTE, bundleOf(
NoteListFragment.NOTE to binding.editText.text.toString()
)
)
findNavController().popBackStack()
}
The only thing to keep in mind is that you need to set the result on the same FragmentManager using the same requestKey. If we take a closer look into the Fragment extension functions, we can see that both setFragmentResult, setFragmentResultListener are calling on parentFragmentManager in the following snippets.
fun Fragment.setFragmentResult(
requestKey: String,
result: Bundle
) = parentFragmentManager.setFragmentResult(requestKey, result)
Another thing that worth mentioning is there can only be a single listener and result for a given key. In our example, if we call setFragmentResult in AddNoteFragment more than once the system will send the most recent result to NoteListFragment before the AddNoteFragment is popped off the back stack.
If the AddNoteFragment happens to be a child Fragment of the NoteListFragment. We just need to set the listener and result on the childFragmentManager instead of parentFragmentManager.
To return results to previous destinations, the Navigation component library uses the SavedStateHandle which is a key-value map that can be used to store and retrieve data persistently through configuration changes
Here we set the value to the savedStateHandle of the previous back stack entry which belongs to the NoteListFragment
Conclusion
I'm excited to see the stable release of these features. In my opinion, the added ability of the Fragment family made working with the Single activity architecture which has all of the destinations represented by Fragment become much easier and more desirable.
What about two setFragmentResultListener?