13

Is there a way to clear navArgs after using them? I have fragment A that opens fragment B with navArgs, then I navigate to fragment C and the user presses back, so fragment B is opened with the same navArgs and I don't want that.

Is there a way to navigate back to fragment B without the navArgs?

Thanks.

Sharas
  • 1,985
  • 3
  • 20
  • 43
  • This looks like a bad design to me. If you don't want the args when you are coming back, what is it you are going to show to the user? Check if you really need this fragment when you are coming back, if not, don't add it in the back stack in the first place. – Ezio Jun 29 '20 at 14:08
  • I agree. I understood it's not the best way for my scenario – Sharas Jun 30 '20 at 06:01

5 Answers5

11

The answer suggested by Juanjo absolutely does work. The only caveat is that you can't use the navArgs property delegate to get at them since it's wrapped Lazy. Instead you just go through the underlying arguments bundle.

For example, in FragmentB

// don't need to pull in the navArgs anymore
// val args: FragmentBArgs by navArgs()

override fun onResume() {
  when (FragmentBArgs.fromBundle(arguments!!).myArg) {
    "Something" -> doSomething()
  }
  // clear it after using it
  arguments!!.clear()
}

// now they are cleared when I go back to this fragment

Matthew Fisher
  • 123
  • 1
  • 6
  • 1
    This is exactly what I needed, thank you! One thing, I'd recommend using safe unwrap on `arguments?` instead of `!!` – Sampson Jul 20 '21 at 15:23
  • What is `arguments` in this case? `when (FragmentBArgs.fromBundle(arguments!!).myArg)` – MML Aug 27 '22 at 02:00
6

Calling arguments?.clear() is not sufficient. Reason for that is that the navArgs() delegate holds all arguments in a local cached variable. Moreover, this variabel is private:

(taken from NavArgsLazy.kt)

private var cached: Args? = null

override val value: Args
    get() {
        var args = cached
        if (args == null) {
            ...
            args = method.invoke(null, arguments) as Args
            cached = args
        }
        return args
    }

I also find this approach pretty stupid. My use-case is a deeplink that navigates the user to a specific menu item of the main screen in my app. Whenever the user comes back to this main screen (no matter wherefrom), the cached arguments are re-used and the user is forced to the deeplinked menu item again.

Since the cached field is private and I don't want to use reflections on this, the only way I see here is to not use navArgs in this case and get the arguments manually the old-school way. By doing so, we can then null them after they were used once:

val navArg = arguments?.get("yourArgument")
if (navArg != null) {
  soSomethingOnce(navArg)
  arguments?.clear()   
}
muetzenflo
  • 5,653
  • 4
  • 41
  • 82
1

I think you could to remove the arguments when your fragment B will be destroyed

You could use the method getArguments().clear(); in onDestroyView() or whenever you want to clear the arguments in your fragment.

Juanjo Berenguer
  • 789
  • 1
  • 5
  • 8
  • 3
    Thanks, but it doesn't work. getArguments() is for arguments passed via Intent, not for navArgs – Sharas Jun 30 '20 at 06:02
  • this actually works well. you two shouldn't blindly assume it doesn't before trying it. if you look at the implementation of the property delegate `navArgs` it actually calls `arguments` (aka: `getArguments()`)" on the fragment. – Ace Nov 01 '20 at 11:42
  • this problem shouldn't even exist in the first place, as these args are usually something we want to be passed only once, and the implementation should take care of clearing them on read – Ace Nov 01 '20 at 11:42
  • there are different suggestions is [this](https://stackoverflow.com/q/17875741) SO questions. I personally cleared them right after checking their value and taking proper action – Ace Nov 01 '20 at 11:46
  • @Sharas can you find a solution? – ysfcyln Dec 15 '20 at 11:37
  • @ysfcyln to be honest, I don't remember :-) – Sharas Dec 15 '20 at 17:11
0

You can simply do navArgs.toBundle().clear()

Abhishek
  • 153
  • 1
  • 2
  • 11
  • 1
    That does not work, because the `cached` variable in the `navArg` implementation will still hold the old value. This old value will then be used again, once you re-visit the fragment. – muetzenflo May 30 '22 at 12:27
0

if you only want to remove one Argument from navArgs you could use

requireArguments().remove("yourArgument")

Remember to use Android:defaultValue inside declaration on your navigation Xml, if you don't do it your app will crash because it won't find the argument previously declared

Mmr100
  • 1