Ever since Android Architecture components came to light, Android development has become much simpler and more efficient. On the other hand, concepts like single source of truth and single activity brought a massive transformation.

With such high-quality development ideas, developers tend to move from traditional activities to effective fragments. This puts many developers in a tricky position(myself included in some cases) regarding data communication between fragments.


Problem

Passing data from one fragment to another without any references (context, interfaces, shared view model, or application-level live data) seems almost impossible. But if you look a bit deeper, you’ll find a solution: targetFragment.

targetFragment provides a way to communicate with the fragments in the back stack. There is no need for any reference to the destination in the source. In simple terms, targetFragment is a way to implement onActivityResult’s functionality in fragments.

How it works

Let’s call showOptionsDialog() in the parent fragment, which shows an OptionsDialogFragment with a list of options. The only new thing we do here is to set the target fragment with a request code:

private fun showOptionsDialog() {
	        val optionsDialogInstance = OptionsDialogFragment()
	        optionsDialogInstance.setTargetFragment(this, 1)
	        optionsDialogInstance.show(childFragmentManager, optionsDialogInstance.tag)
	}

Then we need to implement onActivityResult in the fragment where we need to receive the data. After that, the only thing left is to pass the data from the source fragment. Have a look:

fun onExit(selectionValue: String){
	        val intent = Intent()
	        intent.putExtra("selection",selectionValue)
	        intent.putExtra(TYPE,type)
	        targetFragment?.onActivityResult(targetRequestCode, Activity.RESULT_OK, intent)
	}

Well, this is nice. We can pass data to a fragment without any reference to it. But the problem is targetFragment’s functionality only works when both the source and destination fragments are on the same fragment manager. If a fragment is inflated on the childfragmentmanager, then targetFragmentwon’t work.


Solution

After nearly a decade, the Android team started focusing on this issue. With the release of [Fragment 1.3.0-alpha04](https://developer.android.com/jetpack/androidx/releases/fragment#1.3.0-alpha04), each [FragmentManager](https://developer.android.com/reference/androidx/fragment/app/FragmentManager) now implements [FragmentResultOwner](https://developer.android.com/reference/androidx/fragment/app/FragmentResultOwner). This means that a FragmentManager can act as a central store for fragment results.

This change allows individual fragments to communicate with each other by setting fragment results and listening for those results without requiring fragments to have direct references of each other. Unlike targetFragment, it works across fragment managers.

How it works

First, let’s see how to pass data at the FragmentManagerlevel and then between parent and child FragmentManagers. To pass data to the destination fragment from the source fragment, we have to add a listener with a specific key. Only the bundle that was posted by any other fragments with this key will be invoked here. Have a look:

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
	    super.onViewCreated(view, savedInstanceState)
	    // Use the Kotlin extension in the fragment-ktx artifact
	    setFragmentResultListener("requestKey") { key, bundle ->
	        val result = bundle.getString("name")
	        // Do something with the result...
	    }
	}

Listener in source fragment where we need to retrieve the data

Now, it’s time to pass data from the source fragment, as shown below:

tvSave.setOnClickListener {
	    setResult("requestKey", bundleOf("name" to updatedValue))
	}

Passing data from source fragment using setResult

This is pretty much straightforward and works fine when both fragments are on the same FragmentManagersetResult always delivers the latest data to the destination if you posted multiple times. If the listener is not set at the time of invoking setResult, it’ll store the data and delivery when a listener is assigned. Most importantly, remember that you should only declare a listener with a specific key.

What about communication between child and parent fragments? Well, we do have a solution for that too. To explain it at a high level, child fragments pass data to childfragmentmanager, then it’ll pass the data to parent fragment. The implementation part is similar above. The only difference is that we need to add the listener to the child fragment manager, as shown below:

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
	    super.onViewCreated(view, savedInstanceState)
	    // We set the listener on the child fragmentManager
	    childFragmentManager.setResultListener("uniquerequestKey") { key, bundle ->
	        val result = bundle.getString("name")
	        // Do something with the result..
	    }
	}

Setting a listener in the child fragment

That’s all. Passing data is the same as above. We need to invoke setResultwith a unique key and data with the bundle.

#programming #mobile #kotlin #java #android

A New Way to Pass Data Between Fragments
88.75 GEEK