Table of Contents
Why Fragment Communication Matters
Fragments are meant to be reusable pieces of UI and logic. Very often, multiple fragments must work together inside the same activity. For example, one fragment might show a list of items and another fragment might show the details of a selected item. Fragment communication is about how these pieces share information without becoming tightly coupled.
The main challenge is to allow fragments to communicate while still remaining independent from each other. A fragment should not depend on the concrete implementation of another fragment. Instead, it should only depend on contracts such as interfaces, shared ViewModels, or data passed through the activity.
Important rule: Fragments should not directly manipulate each other’s views or internal state. Instead, communicate through the hosting activity, shared state holders like ViewModel, or defined interfaces and callbacks.
Communicating via the Hosting Activity
A classic way to handle communication is to use the activity that hosts the fragments as a mediator. In this pattern, a fragment notifies the activity about an event, and the activity decides how to react, often by updating or replacing another fragment.
The fragment does not need to know who will use the information. It only calls a method on the activity when something happens, such as a button click or a list item selection.
A fragment can safely access its activity by using requireActivity() or activity. The requireActivity() call throws an exception if the fragment is not attached, which is often preferable during development since it exposes errors early. The activity then can pass data to another fragment using fragment transactions or by calling a method on the other fragment if it has a reference to it.
This style is especially useful when your app uses a single activity with multiple fragments. The activity acts as the central coordinator of navigation and communication.
Using Interfaces for Loose Coupling
If a fragment calls methods directly on its activity, it may become tightly coupled to a specific activity type. To avoid this, you can define an interface inside the fragment that describes what the fragment expects the host to do. Any activity that hosts the fragment must implement this interface.
For example, a fragment that displays a list could define an interface like OnItemSelectedListener with a method that passes back the selected item ID. Inside the fragment, when an item is selected, it calls that method on the interface.
In code, the fragment typically checks in onAttach whether the context implements the required interface. If it does, the fragment stores a reference to it. When an event occurs, the fragment calls the interface method instead of calling an activity method directly.
This approach keeps the fragment independent. The fragment knows only about the interface, not about the concrete activity. You could even reuse the same fragment in different activities as long as they implement the required interface.
When using this pattern, you should also clear the reference to the interface in onDetach to avoid holding a reference to a detached activity.
Passing Data Between Fragments Through Arguments
Fragments have a built-in mechanism for receiving data through a Bundle of arguments. This is useful when one fragment creates another fragment and wants to pass initial data to it, such as an item ID or some configuration.
Instead of exposing a public constructor, a fragment usually exposes a newInstance style method that creates the fragment, creates a Bundle, puts data into that bundle using keys, and sets it as arguments on the fragment. The receiving fragment then reads the arguments in its lifecycle, typically in onCreate.
Since the arguments are stored in the fragment, they survive configuration changes and process recreation as long as the fragment is properly restored by the system. This makes them safer than storing such data only in normal fields.
When fragments are both controlled by the same activity, the activity can also pass data between them by creating a new fragment instance with the required arguments and then replacing the current fragment in the container using a fragment transaction.
Fragment Result API for Direct Communication
To make communication easier and avoid manual interfaces in some cases, the Fragment Result API lets fragments send results to each other through a key based system. Instead of calling methods on the activity, a fragment can set a result associated with a given key, and another fragment can listen for results with the same key.
The fragment that wants to receive data registers a result listener using setFragmentResultListener with a unique request key. It does this while it is attached, usually in onCreate or onViewCreated. The callback receives a Bundle with the result data.
The fragment that wants to send data calls setFragmentResult with the same key and a Bundle. The FragmentManager then delivers this result to the registered listener.
This approach allows communication between fragments that share a FragmentManager, without the fragments directly referencing each other or the activity. It also works correctly with the fragment lifecycle and configuration changes.
Shared ViewModel for State-Based Communication
When your app uses the Architecture Components, a very clean way to handle communication between fragments is through a shared ViewModel. Instead of fragments talking to each other directly, they both observe and modify data exposed by a ViewModel that belongs to their hosting activity.
To set this up, both fragments use ViewModelProvider(requireActivity()) or the Kotlin property delegate by activityViewModels() to obtain the same ViewModel instance. Since the ViewModel is scoped to the activity, both fragments see the same data.
One fragment can update the ViewModel for example by setting a new value on a LiveData. Another fragment observes that LiveData and reacts when the value changes. In this way, user actions in one fragment automatically result in UI updates in another fragment, without direct method calls between them.
This pattern fits very well with MVVM architecture and helps keep fragments relatively simple. The ViewModel becomes the place where business logic and shared state live. It also handles configuration changes, since the ViewModel survives them while fragments are recreated.
Key statement: For complex apps with multiple related fragments, using a shared ViewModel is often the preferred way to communicate, because it keeps fragments decoupled and centralizes shared state.
Handling Back Stack and Navigation-related Data
When using the Navigation Component, communication between fragments often happens alongside navigation operations. Instead of manually performing fragment transactions, you navigate with the NavController. In this case, you typically pass data using Safe Args or a Bundle when navigating to another fragment.
The destination fragment gets the arguments through generated classes when using Safe Args, or through the standard arguments bundle otherwise. This is most useful for one way communication where you simply pass data forward while moving to a new screen.
For returning data to a previous fragment after navigating back, the Fragment Result API or a shared ViewModel is usually a better choice than trying to pass data directly with normal method calls, since the original fragment may be recreated and you may not have a direct reference to it.
The back stack behavior also affects communication. A fragment that expects to receive updates should be prepared that it might be recreated from a previous saved state. If it reads its data from a ViewModel or registers a Fragment Result listener again when recreated, it can still correctly receive or show the latest information after the user navigates back.
Best Practices for Fragment Communication
When designing fragment communication, try to keep each fragment focused on its own UI and immediate logic, and avoid putting navigation or cross fragment dependencies directly inside them. Use the activity or a shared ViewModel as a mediator where appropriate.
Prefer stable, contract based communication. Interfaces, keys for fragment results, argument keys, and ViewModel methods are all contracts that define how fragments interact without knowing details about each other.
Avoid storing direct references to other fragments inside a fragment. Such direct references can easily become invalid when the configuration changes or when the FragmentManager recreates fragments.
Finally, keep in mind that fragments are strongly tied to lifecycle events. Any communication mechanism must respect that lifecycle. Ensure that listeners are registered when the fragment is attached and visible, and are cleared when the fragment is detached or destroyed, especially if you are using manual callbacks or interfaces.