4

Passing data between Fragments in Navigation Component is easy. Say going from A to B you just set arguments with SafeArgs and you are done.

But, it gets tricky when passing data from B back to A.

According to documentation, we can use SharedViewModel which is works well. But I am looking for better way of passing data back to A from B.

The problem of using SharedViewModel is, you have to create SharedViewModel for every fragment pair that you need to pass data.

Any suggestions? If any annotation-processing method you can think about, you are more than welcome to recommend.

musooff
  • 6,412
  • 3
  • 36
  • 65
  • I think, there is no better way to do it. We also used `SharedViewModel` in order to overcome. `Navigation SafeArgs` could be used to pass data from A to B but when it is from B to A, a `ViewModel` seems to be a better way. You can also check out PublishSubject/BehaviorSubject that allows each destination to listen to the latest value. – nuhkoca May 24 '19 at 06:45
  • @nuhkoca musooff sorry if this is little bit out of topic. but I am a beginner. if I use SharedViewModel then I will have 2 viewModels in a fragment ? in FragmentA, I will have FragmentAViewModel and SharedViewModel, and in FragmentB, I will have FragmentBViewModel and SharedViewModel ? so the the shared view model only to pass the data back to previous fragment ? – sarah Mar 31 '20 at 07:36

3 Answers3

2

If you do not want to use SharedViewModel way, you can follow the next approach:

1- Define a delegate for your Details Fragment. (This delegate have to implement Serializable or Parcelable:

interface DetailsFragmentDelegate: Serializable {
    fun onSomething1(someData1: SomeData1)
    fun onSomething2(someData2: SomeData2)
}

2- Add the delegate to your Details Fragment arguments in nav_graph.xml

3- Pass the delegate to your Details Fragment when navigating to its destination by your Base Fragment:

findNavController().navigate(
    BaseFragmentDirections.actionBaseFragmentToDetailsFragment(
        object: DetailsFragmentDelegate {
            // override delegate methods

        }
    )
)

4- Get the delegate argument in Details Fragment and pass the data back wherever you need:

....
delegate.onSomething1(data1)
....
delegate.onSomething2(data2)
....

I am not sure whether there is a better way or not, but it's working...

Mohanad Refaai
  • 327
  • 1
  • 9
0

You don't need to create a ViewModel per Fragment pair. What I am doing is creating a ViewModel per Fragment. Each ViewModel would have a map[Class[Fragment], Any] named mailBox.

Each Fragment will define a FragmentResult type which is different per Fragment class.

In the child Fragment onBackPressedHandler, before pop-up, fetch the parent ViewModel from the Activity and put your result in the mailBox for your class. You will need a ViewModel class for that. See below.

The parent Fragment needs to pass it's ViewModel.class to the child Fragment, before launching it.

When the Parent Fragment is re-started after popping up the child from the stack. Get the mailBox map from it's ViewModel, check if there is a key with value from the expected FragmentChild::class. If so, then cast to the desired type.

The parent Fragment ViewModel needs to save who was the last child it launched.

Pablo Valdes
  • 734
  • 8
  • 19
-1

I am using an callback interface for this. So i have created an interface with some methods. I implemented that interface 'A' and then call if from 'B'. Very easy and works great.

Michel
  • 73
  • 9
  • @LMaker: sorry for the late relpy but here some code Create an interface (File->New->Java Class->Kind = interface) `public interface FragmentDataPassListener { void doSomeThing(String parameterA); }` – Michel Jun 30 '19 at 10:28
  • extended my Activity like this `public class SomeActivity extends AppCompatActivity implements FragmentDataPassListener { // normal activity methodes here ... // put the overrides of your interface here @Override public void doSomeThing(String parameterA) { } }` – Michel Jun 30 '19 at 10:30
  • Then in my fragment i do this. `@Override public void onAttach(Context context) { super.onAttach(context); try { mCallback = (FragmentDataPassListener) context; } catch (ClassCastException e) { throw new ClassCastException(context.toString()+ " must implement FragmentDataPassListener in GPSmapFragment"); } }` Now i can use mCallback in my fragment to execute the methods in the Activity by just `calling mCallback.doSomeThing("Test");` – Michel Jun 30 '19 at 10:31
  • Don't do that !!. it will cause a Memory Leaks and this Approach is not recommended. – Mohamed AbdelraZek Nov 04 '19 at 17:24