Appyx vs Jetpack Compose Navigation

Andrey Kovalev
Bumble Tech
5 min readAug 12, 2022

--

The Bumble team has just recently released a navigation framework: Appyx

See the intro article here:

Appyx is a fundamentally different approach compared to how you would do things with Jetpack Compose Navigation!

In this article, we’ll focus on a few use-cases and compare them in practice.

Use-case I — Simple navigation case

Let’s take a look at this simple navigation example where we navigate from Home screen to Search screen:

This approach has a few drawbacks:

  1. String based arguments which need to form a valid route. Developers are responsible for managing all routes themselves. They need to keep in mind what arguments are required by a particular screen, as well as keys, data types, and their order.
    They must not forget to update code manually whenever something changes, as route validation happens only at runtime and can result in unexpected crashes.
  2. Separation of concerns. Navigation comes from the “view” layer instead of business logic. This means that we have to define our UI implementation next to navigation.
  3. Testability. Because our navigation is defined in the composition, we can’t simply unit-test.

Let’s take a look at how we can do it with Appyx. First, let’s define our navigation targets with classes:

Then implement UI for your navigation targets in a Node class:

Then we define how those NavTargets relate to Nodes, and where to draw them in the Composable UI:

To navigate to the Search screen simply add an appropriate NavTarget to BackStack:

This approach addresses the problems mentioned above:

  1. NavTargets. Your navigation targets are defined as classes so that you have compiled time checks for your arguments.
  2. Navigation is separated from the UI.
  3. Testable. BackStack is a Kotlin class so to test your navigation you just need to verify that BackStack is in the correct state.

As a bonus — it’s also recomposition safe. Only the View method is a composable function, the Node itself is not. So, it’s safe to provide/inject your dependencies without worrying about recomposition.

Use-case II — Transition animations

Unfortunately, Jetpack Compose Navigation comes with a non-configurable crossfade animation. For anything more custom, you need to use accompanist-navigation:

But this also has drawbacks:

  1. Internally, the navigation library internally always uses BackStack. Therefore, only enterTransition, exitTransition, popEnterTransition, popExitTransition are available. Further customisations are not possible. We’ll see use-cases for this later in this article.
  2. For animation you can only use properties defined in TransitionData class:

This Appyx example animates colour and rotation. It would not be possible to achieve this with TransitionData:

In Appyx transition animations can be added with a one-liner:

Let’s take a look under the hood at the BackStackSlider TransitionHandler:

  1. Transitions are defined in a separate class, so keep your main composables cleaner.
  2. TransitionHandler returns a Modifier which will be applied to a child Node composable. This is extremely powerful — properties like colour and rotation are just the start. You can use anything and everything that can be animated with a Modifier!
  3. Transitions are not limited to the enterTransition, exitTransition, popEnterTransition, popExitTransition used in Jetpack Compose Navigation’s BackStack. We can define custom animations for any back stack operation (e.g. replace, new root, single top, etc.), or between any two back stack states.

Use-case III— No alternative to a back stack

So far in both Jetpack Navigation and Appyx we used BackStack as a navigation engine.

But this is not always what we need! For instance, we may want to switch back and forth between navigation targets without re-creating UI — like a ViewPager:

Unfortunately, this is not possible with Jetpack Compose Navigation as it internally uses solely BackStack.

Instead of a hardcoded BackStack Appyx provides an abstraction NavModel:

where BackStack is just one of many possible implementations, and to achieve ViewPager-like navigation we just need to implement a different navigation model.

Navigation models provide the ability to implement anything you can think of, easily. It might be an enhanced ViewPager like this one which allows us to switch between pager and carousel modes:

This one is also animated using a TransitionHandler like we did for our back stack — as you can see, it’s a much more versatile approach with a lot more freedom compared to the fixed set of animations in Jetpack Compose Navigation.

How do you implement these more custom navigation models? We’ll return to this in another article and give a step-by-step explanation.

Want to know more about NavModel right now? Check out our official documentation here.

Conclusion

I hope you found this article interesting, and that it will help you to look at navigation from a different angle! Let’s summarise the key features of Appyx when dealing with navigation in Jetpack Compose:

  1. Type-safe arguments for NavTargets and compile-time checks
  2. Navigation and UI are separated
  3. Navigation can be unit-tested
  4. Nodes are the perfect place to inject your dependencies without worrying about re-composition
  5. Powerful transitions that are not restricted by the TransitionData parameters
  6. Navigation models are not limited to BackStack only

In the meantime:

Don’t miss our next article in the series

For more updates follow us on Twitter @BumbleEng @andreyk_nn @ZsoltKocsi

--

--

Andrey Kovalev
Bumble Tech

Android developer @ Bumble. Working with Jetpack Compose and building internal libraries