Communicate with fragments

To reuse fragments, build them as completely self-contained components that define their own layout and behavior. Once you define these reusable fragments, you can associate them with an activity and connect them with the application logic to realize the overall composite UI.

To properly react to user events and to share state information, you often need to have channels of communication between an activity and its fragments or between two or more fragments. To keep fragments self-contained, don't have fragments communicate directly with other fragments or with their host activity.

The Fragment library provides two options for communication: a shared ViewModel and the Fragment Result API. The recommended option depends on the use case. To share persistent data with custom APIs, use a ViewModel. For a one-time result with data that can be placed in a Bundle, use the Fragment Result API.

The following sections show you how to use ViewModel and the Fragment Result API to communicate between your fragments and activities.

Share data using a ViewModel

ViewModel is an ideal choice when you need to share data between multiple fragments or between fragments and their host activity. ViewModel objects store and manage UI data. For more information about ViewModel, see ViewModel overview.

Share data with the host activity

In some cases, you might need to share data between fragments and their host activity. For example, you might want to toggle a global UI component based on an interaction within a fragment.

Consider the following ItemViewModel:

Kotlin

classItemViewModel:ViewModel(){
privatevalmutableSelectedItem=MutableLiveData<Item>()
valselectedItem:LiveData<Item>get()=mutableSelectedItem
funselectItem(item:Item){
mutableSelectedItem.value=item
}
}

Java

publicclass ItemViewModelextendsViewModel{
privatefinalMutableLiveData<Item>selectedItem=newMutableLiveData<Item>();
publicvoidselectItem(Itemitem){
selectedItem.setValue(item);
}
publicLiveData<Item>getSelectedItem(){
returnselectedItem;
}
}

In this example, the stored data is wrapped in a MutableLiveData class. LiveData is a lifecycle-aware observable data holder class. MutableLiveData lets its value be changed. For more information about LiveData, see LiveData overview.

Both your fragment and its host activity can retrieve a shared instance of a ViewModel with activity scope by passing the activity into the ViewModelProvider constructor. The ViewModelProvider handles instantiating the ViewModel or retrieving it if it already exists. Both components can observe and modify this data.

Kotlin

classMainActivity:AppCompatActivity(){
// Using the viewModels() Kotlin property delegate from the activity-ktx
// artifact to retrieve the ViewModel in the activity scope.
privatevalviewModel:ItemViewModelbyviewModels()
overridefunonCreate(savedInstanceState:Bundle?){
super.onCreate(savedInstanceState)
viewModel.selectedItem.observe(this,Observer{item->
// Perform an action with the latest item data.
})
}
}
classListFragment:Fragment(){
// Using the activityViewModels() Kotlin property delegate from the
// fragment-ktx artifact to retrieve the ViewModel in the activity scope.
privatevalviewModel:ItemViewModelbyactivityViewModels()
// Called when the item is clicked.
funonItemClicked(item:Item){
// Set a new item.
viewModel.selectItem(item)
}
}

Java

publicclass MainActivityextendsAppCompatActivity{
privateItemViewModelviewModel;
@Override
publicvoidonCreate(BundlesavedInstanceState){
super.onCreate(savedInstanceState);
viewModel=newViewModelProvider(this).get(ItemViewModel.class);
viewModel.getSelectedItem().observe(this,item->{
// Perform an action with the latest item data.
});
}
}
publicclass ListFragmentextendsFragment{
privateItemViewModelviewModel;
@Override
publicvoidonViewCreated(@NonNullViewview,BundlesavedInstanceState){
super.onViewCreated(view,savedInstanceState);
viewModel=newViewModelProvider(requireActivity()).get(ItemViewModel.class);
...
items.setOnClickListener(item->{
// Set a new item.
viewModel.select(item);
});
}
}

Share data between fragments

Two or more fragments in the same activity often need to communicate with each other. For example, imagine one fragment that displays a list and another that lets the user apply various filters to the list. Implementing this case isn't trivial without the fragments communicating directly, but then they are no longer self-contained. Additionally, both fragments must handle the scenario where the other fragment is not yet created or visible.

These fragments can share a ViewModel using their activity scope to handle this communication. By sharing the ViewModel in this way, the fragments don't need to know about each other, and the activity doesn't need to do anything to facilitate the communication.

The following example shows how two fragments can use a shared ViewModel to communicate:

Kotlin

classListViewModel:ViewModel(){
valfilters=MutableLiveData<Set<Filter>>()
privatevaloriginalList:LiveData<List<Item>>()=...
valfilteredList:LiveData<List<Item>>=...
funaddFilter(filter:Filter){...}
funremoveFilter(filter:Filter){...}
}
classListFragment:Fragment(){
// Using the activityViewModels() Kotlin property delegate from the
// fragment-ktx artifact to retrieve the ViewModel in the activity scope.
privatevalviewModel:ListViewModelbyactivityViewModels()
overridefunonViewCreated(view:View,savedInstanceState:Bundle?){
viewModel.filteredList.observe(viewLifecycleOwner,Observer{list->
// Update the list UI.
}
}
}
classFilterFragment:Fragment(){
privatevalviewModel:ListViewModelbyactivityViewModels()
overridefunonViewCreated(view:View,savedInstanceState:Bundle?){
viewModel.filters.observe(viewLifecycleOwner,Observer{set->
// Update the selected filters UI.
}
}
funonFilterSelected(filter:Filter)=viewModel.addFilter(filter)
funonFilterDeselected(filter:Filter)=viewModel.removeFilter(filter)
}

Java

publicclass ListViewModelextendsViewModel{
privatefinalMutableLiveData<Set<Filter>>filters=newMutableLiveData<>();
privatefinalLiveData<List<Item>>originalList=...;
privatefinalLiveData<List<Item>>filteredList=...;
publicLiveData<List<Item>>getFilteredList(){
returnfilteredList;
}
publicLiveData<Set<Filter>>getFilters(){
returnfilters;
}
publicvoidaddFilter(Filterfilter){...}
publicvoidremoveFilter(Filterfilter){...}
}
publicclass ListFragmentextendsFragment{
privateListViewModelviewModel;
@Override
publicvoidonViewCreated(@NonNullViewview,BundlesavedInstanceState){
super.onViewCreated(view,savedInstanceState);
viewModel=newViewModelProvider(requireActivity()).get(ListViewModel.class);
viewModel.getFilteredList().observe(getViewLifecycleOwner(),list->{
// Update the list UI.
});
}
}
publicclass FilterFragmentextendsFragment{
privateListViewModelviewModel;
@Override
publicvoidonViewCreated(@NonNullViewview,BundlesavedInstanceState){
viewModel=newViewModelProvider(requireActivity()).get(ListViewModel.class);
viewModel.getFilters().observe(getViewLifecycleOwner(),set->{
// Update the selected filters UI.
});
}
publicvoidonFilterSelected(Filterfilter){
viewModel.addFilter(filter);
}
publicvoidonFilterDeselected(Filterfilter){
viewModel.removeFilter(filter);
}
}

Both fragments use their host activity as the scope for the ViewModelProvider. Because the fragments use the same scope, they receive the same instance of the ViewModel, which enables them to communicate back and forth.

Share data between a parent and child fragment

When working with child fragments, your parent fragment and its child fragments might need to share data with each other. To share data between these fragments, use the parent fragment as the ViewModel scope, as shown in the following example:

Kotlin

classListFragment:Fragment(){
// Using the viewModels() Kotlin property delegate from the fragment-ktx
// artifact to retrieve the ViewModel.
privatevalviewModel:ListViewModelbyviewModels()
overridefunonViewCreated(view:View,savedInstanceState:Bundle?){
viewModel.filteredList.observe(viewLifecycleOwner,Observer{list->
// Update the list UI.
}
}
}
classChildFragment:Fragment(){
// Using the viewModels() Kotlin property delegate from the fragment-ktx
// artifact to retrieve the ViewModel using the parent fragment's scope
privatevalviewModel:ListViewModelbyviewModels({requireParentFragment()})
...
}

Java

publicclass ListFragmentextendsFragment{
privateListViewModelviewModel;
@Override
publicvoidonViewCreated(@NonNullViewview,BundlesavedInstanceState){
viewModel=newViewModelProvider(this).get(ListViewModel.class);
viewModel.getFilteredList().observe(getViewLifecycleOwner(),list->{
// Update the list UI.
}
}
}
publicclass ChildFragmentextendsFragment{
privateListViewModelviewModel;
@Override
publicvoidonViewCreated(@NonNullViewview,BundlesavedInstanceState){
viewModel=newViewModelProvider(requireParentFragment()).get(ListViewModel.class);
...
}
}

Scope a ViewModel to the Navigation Graph

If you're using the Navigation library, you can also scope a ViewModel to the lifecycle of a destination's NavBackStackEntry. For example, the ViewModel can be scoped to the NavBackStackEntry for the ListFragment:

Kotlin

classListFragment:Fragment(){
// Using the navGraphViewModels() Kotlin property delegate from the fragment-ktx
// artifact to retrieve the ViewModel using the NavBackStackEntry scope.
// R.id.list_fragment == the destination id of the ListFragment destination
privatevalviewModel:ListViewModelbynavGraphViewModels(R.id.list_fragment)
overridefunonViewCreated(view:View,savedInstanceState:Bundle?){
viewModel.filteredList.observe(viewLifecycleOwner,Observer{item->
// Update the list UI.
}
}
}

Java

publicclass ListFragmentextendsFragment{
privateListViewModelviewModel;
@Override
publicvoidonViewCreated(@NonNullViewview,BundlesavedInstanceState){
NavControllernavController=NavHostFragment.findNavController(this);
NavBackStackEntrybackStackEntry=navController.getBackStackEntry(R.id.list_fragment)
viewModel=newViewModelProvider(backStackEntry).get(ListViewModel.class);
viewModel.getFilteredList().observe(getViewLifecycleOwner(),list->{
// Update the list UI.
}
}
}

For more information about scoping a ViewModel to a NavBackStackEntry, see Interact programmatically with the Navigation component.

Get results using the Fragment Result API

In some cases, you might want to pass a one-time value between two fragments or between a fragment and its host activity. For example, you might have a fragment that reads QR codes, passing the data back to a previous fragment.

In Fragment version 1.3.0 and higher, each FragmentManager implements FragmentResultOwner. This means that a FragmentManager can act as a central store for fragment results. This change lets components communicate with each other by setting fragment results and listening for those results, without requiring those components to have direct references to each other.

Pass results between fragments

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 setFragmentResultListener() on fragment A's FragmentManager, as shown in the following example:

Kotlin

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

Java

@Override
publicvoidonCreate(@NullableBundlesavedInstanceState){
super.onCreate(savedInstanceState);
getParentFragmentManager().setFragmentResultListener("requestKey",this,newFragmentResultListener(){
@Override
publicvoidonFragmentResult(@NonNullStringrequestKey,@NonNullBundlebundle){
// We use a String here, but any type that can be put in a Bundle is supported.
Stringresult=bundle.getString("bundleKey");
// Do something with the result.
}
});
}
fragment b sends data to fragment a using a FragmentManager
Figure 1. Fragment B sends data to fragment A using a FragmentManager.

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

Kotlin

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

Java

button.setOnClickListener(newView.OnClickListener(){
@Override
publicvoidonClick(Viewv){
Bundleresult=newBundle();
result.putString("bundleKey","result");
getParentFragmentManager().setFragmentResult("requestKey",result);
}
});

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

You can have only a single listener and result for a given key. If you call setFragmentResult() more than once for the same key, and if the listener is not STARTED, the system replaces any pending results with your updated result.

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. 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 then fires immediately.

Test 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 isn't being tested.

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

@Test
funtestFragmentResultListener(){
valscenario=launchFragmentInContainer<ResultListenerFragment>()
scenario.onFragment{fragment->
valexpectedResult="result"
fragment.parentFragmentManager.setFragmentResult("requestKey",bundleOf("bundleKey"toexpectedResult))
assertThat(fragment.result).isEqualTo(expectedResult)
}
}
classResultListenerFragment:Fragment(){
varresult:String?=null
overridefunonCreate(savedInstanceState:Bundle?){
super.onCreate(savedInstanceState)
// Use the Kotlin extension in the fragment-ktx artifact.
setFragmentResultListener("requestKey"){requestKey,bundle->
result=bundle.getString("bundleKey")
}
}
}

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

@Test
funtestFragmentResult(){
valscenario=launchFragmentInContainer<ResultFragment>()
lateinitvaractualResult:String?
scenario.onFragment{fragment->
fragment.parentFragmentManager
.setFragmentResultListener("requestKey"){requestKey,bundle->
actualResult=bundle.getString("bundleKey")
}
}
onView(withId(R.id.result_button)).perform(click())
assertThat(actualResult).isEqualTo("result")
}
classResultFragment:Fragment(R.layout.fragment_result){
overridefunonViewCreated(view:View,savedInstanceState:Bundle?){
view.findViewById(R.id.result_button).setOnClickListener{
valresult="result"
// Use the Kotlin extension in the fragment-ktx artifact.
setFragmentResult("requestKey",bundleOf("bundleKey"toresult))
}
}
}

Pass results between parent and child fragments

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

Kotlin

overridefunonCreate(savedInstanceState:Bundle?){
super.onCreate(savedInstanceState)
// Set the listener on the child fragmentManager.
childFragmentManager.setFragmentResultListener("requestKey"){key,bundle->
valresult=bundle.getString("bundleKey")
// Do something with the result.
}
}

Java

@Override
publicvoidonCreate(@NullableBundlesavedInstanceState){
super.onCreate(savedInstanceState);
// Set the listener on the child fragmentManager.
getChildFragmentManager()
.setFragmentResultListener("requestKey",this,newFragmentResultListener(){
@Override
publicvoidonFragmentResult(@NonNullStringrequestKey,@NonNullBundlebundle){
Stringresult=bundle.getString("bundleKey");
// Do something with the result.
}
});
}
a child fragment can use FragmentManager 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{
valresult="result"
// Use the Kotlin extension in the fragment-ktx artifact.
setFragmentResult("requestKey",bundleOf("bundleKey"toresult))
}

Java

button.setOnClickListener(newView.OnClickListener(){
@Override
publicvoidonClick(Viewv){
Bundleresult=newBundle();
result.putString("bundleKey","result");
// The child fragment needs to still set the result on its parent fragment manager.
getParentFragmentManager().setFragmentResult("requestKey",result);
}
});

Receive results in the host activity

To receive a fragment result in the host activity, set a result listener on the fragment manager using getSupportFragmentManager().

Kotlin

classMainActivity:AppCompatActivity(){
overridefunonCreate(savedInstanceState:Bundle?){
super.onCreate(savedInstanceState)
supportFragmentManager
.setFragmentResultListener("requestKey",this){requestKey,bundle->
// We use a String here, but any type that can be put in a Bundle is supported.
valresult=bundle.getString("bundleKey")
// Do something with the result.
}
}
}

Java

class MainActivityextendsAppCompatActivity{
@Override
publicvoidonCreate(@NullableBundlesavedInstanceState){
super.onCreate(savedInstanceState);
getSupportFragmentManager().setFragmentResultListener("requestKey",this,newFragmentResultListener(){
@Override
publicvoidonFragmentResult(@NonNullStringrequestKey,@NonNullBundlebundle){
// We use a String here, but any type that can be put in a Bundle is supported.
Stringresult=bundle.getString("bundleKey");
// Do something with the result.
}
});
}
}

Content and code samples on this page are subject to the licenses described in the Content License. Java and OpenJDK are trademarks or registered trademarks of Oracle and/or its affiliates.

Last updated 2026年02月26日 UTC.