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
setResultListener("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...
}
});
}
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
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");
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
STARTEDwhen 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..
}
});
}
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))
}
}
}

