45

I've started using Android Architecture Components (Navigation and Safe Args, View Models) along with Koin library.

Currently, I've got a problem with passing arguments between two fragments - I need to pass a string value from fragment A to fragment B, modify this value in fragment B and pass it back to fragment A.

I've found one possible solution to my problem - shared view models. Unfortunately, this approach has one problem because I can pass and modify values between screens, but when the fragment A navigate to another destination the value in the shared view model is still stored and not cleared.

Is there any different solution of passing and modifying data between fragments in Android Navigation? I want to avoid clearing this one value by hand (when the fragment A is destroyed).

RKRK
  • 1,284
  • 5
  • 14
  • 18
rubin94
  • 502
  • 1
  • 5
  • 12

5 Answers5

72

Android just released a solution for this; Passing data between Destinations (Navigation 2.3.0-alpha02), basically, in fragment A you observe changes in a variable and in fragment B you change that value before executing popBackStack().

Fragment A:

findNavController().currentBackStackEntry?.savedStateHandle?.getLiveData<String>("key")?.observe(viewLifecycleOwner) { result ->
    // Do something with the result.
}

Fragment B:

navController.previousBackStackEntry?.savedStateHandle?.set("key", result)
navController.popBackStack()
Johann
  • 27,536
  • 39
  • 165
  • 279
zgluis
  • 3,021
  • 2
  • 17
  • 22
  • Thanks! It works but this is very weird solution as compared to when you are passing data from Fragment A to Fragment B. This solution is more like old way of passing data using bundle as we are used used to do in Java. – Zeeshan Jun 21 '21 at 19:45
  • can we pass multiple data using this? – Richa Nov 25 '21 at 16:29
  • Good job!!! Keep it up – VVB Jan 05 '22 at 10:47
  • 8
    Note: SavedStateHandle which Hilt injects into ViewModel and NavBackStackEntry SavedStateHandle are not the same objects. I spent several hours figuring this out – t3ddys Jan 30 '22 at 23:17
  • I needed a way to have an onBackPressed event in the Viewmodel. I searched for many many hours. This is the way. – Chucky Feb 14 '22 at 16:32
  • This solution doesn't work on the first use but works thereafter. The solution that I found works is: https://stackoverflow.com/a/61920374/753632 – Johann Nov 20 '22 at 15:50
  • Using this logic, I am able navigate back to fragment A with the arguments, but after that it won't let me go back to fragment B. It says the navController.currentDestination = null. – Mayur More Jul 20 '23 at 12:25
2

You can use Fragment Result API.

Fragment A -> Fragment B

In Fragment A :

binding.buttonGo.setOnClickListener {
   setFragmentResultListener(ADD_LOCATION) { key, bundle ->
       clearFragmentResultListener(requestKey = ADD_LOCATION)
       val selectedLocationModel =
           bundle.getParcelable<LocationModel>(SELECTED_LOCATION_MODEL)
       this.selectedLocationModel = selectedLocationModel
   }
   navToFragmentB()
}

In Fragment B:

setFragmentResult(
    ADD_LOCATION,
    bundleOf(SELECTED_LOCATION_MODEL to selectedLocationModel)
)
goBack()

Do not forget to call clearFragmentResultListener() before create new one.

furkanbzkurt
  • 296
  • 4
  • 14
1

Currently, I've got a problem with passing arguments between two fragments - I need to pass a string value from fragment A to fragment B, modify this value in fragment B and pass it back to fragment A.

The theoretical solution really is to have the two fragments in a shared <navigation tag, then scope the ViewModel to the ID of the navigation tag, this way you now share the ViewModel between the two screens.

To make this reliable, it's best to use the NavBackStackEntry of the Navigation tag as both a ViewModelStoreOwner and SavedStateRegistryOwner, and create an AbstractSavedStateViewModelFactory that will create the ViewModel using the ViewModelProvider, while also giving you a SavedStateHandle.

You can communicate the results from FragmentB to FragmentA using this SavedStateHandle, associated with the shared ViewModel (scoped to the shared NavGraph).

EpicPandaForce
  • 79,669
  • 27
  • 256
  • 428
0

You can try this solution

<fragment
    android:id="@+id/a"
    android:name="...">

    <argument
        android:name="text"
        app:argType="string" />

    <action
        android:id="@+id/navigate_to_b"
        app:destination="@id/b" />

</fragment>

<fragment
    android:id="@+id/b"
    android:name="...">

    <argument
        android:name="text"
        app:argType="string" />

    <action
        android:id="@+id/return_to_a_with_arguments"
        app:destination="@id/a"
        app:launchSingleTop="true"
        app:popUpTo="@id/b"
        app:popUpToInclusive="true" />

</fragment>

and navigation fragment

NavHostFragment.findNavController(this).navigate(BFragmentDirections.returnToAWithArguments(text))

ianhanniballake`s comment has helped me solve a similar problem

-7

1) Pass string from Fragment A to Fragment B with action_A_to_B and SafeArgs.

2) popBackStack to remove Fragment B.

navController.popBackStack(R.id.AFragment, false);

or

navController.popBackStack();

3) Then pass modified data from B to A with action_B_to_A.

EDIT.

Here you have some another solution

Ruan_Lopes
  • 1,381
  • 13
  • 18
Klaudia
  • 7
  • 4
  • Your answer does not answer the passing back the arguments. It just remove the fragment from the navcontroller, that is it. Please reconsider your answer. – Duna Sep 12 '22 at 12:30