Friday 29 January 2021

Replacing destination using NavGraph (transaction when using app:popUpTo)

We need to implement a state-machine driven navigation, where the currently shown screen depends on current state. As such, our approach so far has been to use global actions and navigate to the right screen whenever the state changes. On first look, everything seemed to work great, however, we've started noticing some weird behaviours when using the app:popUpTo attribute on the actions.

In our concrete case, we have 2 sub nav graphs (let's call them NG1 and NG2), where NG2 is state-machine driven. Let's say we're in screen A in NG1 and then we navigate to NG2, which adds screen B. We now have 2 options:

  1. If we navigate to screen C (in NG2) using an action with app:popUpTo="@+id/NG2" (effectively aiming to replace B with C), then, sometimes, Fragment A briefly starts (onStart) for a few tens of milliseconds and then stops, before starting C. So instead of B -> C, we get B -> A -> C. It seems that an action with a popUpTo, is not transactional.
  2. If we navigate to screen C (in NG2) using an action without app:popUpTo="@+id/NG2", screen A does not start, but B is left in the back stack. As such, if screen B needs to be shown again, we end up with a backstack looking like A / B / C / B, which is not what we're aiming for.

Of course, the behaviour we're looking for is the one attempted in 1, where we can move from a screen X to a screen Y to a screen Z, with popping Y, but not briefly starting X. Is there anything we're doing wrong or is it a bug in the android-navigation component?


I'm also adding some logs, showing actual logs that I've just replicated (timestamps are actual):

// Here we navigate from A to B
2020-03-09 19:14:14.999 BaseAppNavigator: Navigating to directions: com.example.app:id/toNavGraph2
2020-03-09 19:14:15.006 BaseAppNavigator.OnDestinationChangedListener: Navigation destination changed: com.example.app:id/fragmentB
2020-03-09 19:14:15.007 BaseAppNavigator.OnDestinationChangedListener: Active navigation graph changed to: com.example.app:id/navGraph2
2020-03-09 19:14:15.235 BaseViewModelFragment: OnStart: FragmentB
2020-03-09 19:14:15.247 BaseViewModelFragment: OnStop: FragmentA

// Here we trigger navigate from B to C, with a destination with popUpTo="navGraph2"
2020-03-09 19:14:56.422 BaseAppNavigator: Navigating to directions: com.example.app:id/toFragmentC
2020-03-09 19:14:56.430 BaseAppNavigator.OnDestinationChangedListener: Navigation destination changed: com.example.app:id/fragmentC
2020-03-09 19:14:56.516 BaseViewModelFragment: OnStart: FragmentA
2020-03-09 19:14:56.529 BaseViewModelFragment: OnStop: FragmentB
2020-03-09 19:14:56.537 BaseViewModelFragment: OnDestroy: FragmentB
2020-03-09 19:14:56.580 BaseViewModelFragment: OnStart: FragmentC
2020-03-09 19:14:56.613 BaseViewModelFragment: OnStop: FragmentA

with the following action:

<action
      android:id="@+id/toFragmentC"
      app:destination="@id/fragmentC"
      app:popUpTo="@+id/NavGraph2" />


from Replacing destination using NavGraph (transaction when using app:popUpTo)

No comments:

Post a Comment