Google is committed to advancing racial equity for Black communities. See how.

Pass data between fragments

Starting with Fragment 1.3.0-alpha04, each FragmentManager implements FragmentResultOwner. This means that a FragmentManager can act as a central store for fragment results. This change allows separate fragments to communicate with each other by setting fragment results and listening for those results without requiring fragments to have direct references to each other.

To pass data back to fragment A from fragment B, first set a result listener on fragment A, the fragment that receives the result. Call the setFragmentResultListener() API on fragment A's FragmentManager, as shown in the following example:

Kotlin

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    // Use the Kotlin extension in the fragment-ktx artifact
    setFragmentResultListener("requestKey") { key, bundle ->
        // We use a String here, but any type that can be put in a Bundle is supported
        val result = bundle.getString("bundleKey")
        // Do something with the result...
    }
}

Java

@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    getParentFragmentManager().setFragmentResultListener("key", this, new FragmentResultListener() {
        @Override
        public void onFragmentResult(@NonNull String key, @NonNull Bundle bundle) {
            // We use a String here, but any type that can be put in a Bundle is supported
            String result = bundle.getString("bundleKey");
            // Do something with the result...
        }
    });
}
fragment b sends data to fragment a using a fragment manager
Figure 1. Fragment B sends data to fragment A using a FragmentManager.

In fragment B, the fragment producing the result, you must set the result on the same FragmentManager, using the same requestKey. You can do so by using the setFragmentResult() API:

Kotlin

button.setOnClickListener {
    val result = "result"
    // Use the Kotlin extension in the fragment-ktx artifact
    setFragmentResult("requestKey", bundleOf("bundleKey" to result))
}

Java

button.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        Bundle result = new Bundle();
        result.putString("bundleKey", "result");
        getParentFragmentManager().setFragmentResult("requestKey", result);
    }
});

Fragment A then receives the result and executes the listener callback once it is STARTED.

You can have only a single listener and result for a given key. If you call setResult() more than once for the same key, the system sends fragment A the most recent result before fragment B is popped off the back stack. If you set a result without a corresponding listener to receive it, the result is stored in the FragmentManager until you set a listener with the same key. Note that the listener's fragment must be STARTED before the fragment can receive the result. Once a listener receives a result and fires the onFragmentResult() callback, the result is cleared. This behavior has two major implications:

  • Fragments on the back stack do not receive results until they have been popped and are STARTED.
  • If a fragment listening for a result is STARTED when the result is set, the listener's callback is then fired immediately.

Passing results between parent and child fragments

To pass a result from a child fragment to a parent, the parent fragment should use getChildFragmentManager() instead of getParentFragmentManager() when calling setFragmentResultListener().

Kotlin

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

Java

@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    // We set the listener on the child fragmentManager
    getChildFragmentManager().setFragmentResultListener("key", this, new FragmentResultListener() {
        @Override
        public void onFragmentResult(@NonNull String key, @NonNull Bundle bundle) {
            String result = bundle.getString("bundleKey");
            // Do something with the result..
        }
    });
}
a child fragment can use fragment manager to send a result to
            its parent
Figure 2. A child fragment can use FragmentManager to send a result to its parent.

The child fragment sets the result on its FragmentManager. The parent then receives the result once the fragment is STARTED:

Kotlin

button.setOnClickListener {
    val result = "result"
    // Use the Kotlin extension in the fragment-ktx artifact
    setResult("requestKey", bundleOf("bundleKey" to result))
}

Java

button.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        Bundle result = new Bundle();
        result.putString("bundleKey", "result");

        // The child fragment needs to still set the result on its parent fragment manager
        getParentFragmentManager().setFragmentResult("requestKey", result);
    }
});

Testing fragment results

Use FragmentScenario to test calls to setFragmentResult() and setFragmentResultListener(). Create a scenario for the fragment under test by using launchFragmentInContainer or launchFragment, and then manually call the method that is not being tested.

To test setResultListener(), create a scenario with the fragment that makes the call to setResultListener(). Next, call setResult() directly, and verify the result:

@Test
fun testFragmentResultListener() {
    val scenario = launchFragmentInContainer<ResultListenerFragment>()
    scenario.onFragment { fragment ->
        val expectedResult = "result"
        fragment.parentFragmentManagager.setResult("requestKey", bundleOf("bundleKey" to expectedResult))
        assertThat(fragment.result).isEqualTo(expectedResult)
    }
}

class ResultListenerFragment : Fragment() {
    var result : String? = null
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // Use the Kotlin extension in the fragment-ktx artifact
        setResultListener("requestKey") { key, bundle ->
            result = bundle.getString("bundleKey")
        }
    }
}

To test setResult(), create a scenario with the fragment that makes the call to setResult(). Next, call setResultListener() directly, and verify the result:

@Test
fun testFragmentResult() {
    val scenario = launchFragmentInContainer<ResultFragment>()
    lateinit var actualResult: String?
    scenario.onFragment { fragment ->
        fragment.parentFragmentManagager.setResultListener("requestKey") { key, bundle ->
            actualResult = bundle.getString("bundleKey")
        }
    }
    onView(withId(R.id.result_button)).perform(click())
    assertThat(actualResult).isEqualTo("result")
}

class ResultFragment : Fragment(R.layout.fragment_result) {
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        view.findViewById(R.id.result_button).setOnClickListener {
            val result = "result"
            // Use the Kotlin extension in the fragment-ktx artifact
            setResult("requestKey", bundleOf("bundleKey" to result))
        }
    }
}