Know the pitfalls of Navigation Compose
When you’re studying this text, you’re most likely pondering:
“Hmm, I ponder what to make use of to navigate in my Jetpack Compose app. Ought to I exploit navigation-compose as recommended by Google or follow Fragments and use Compose just for rendering views?”
On this article, I’m going to speak about my expertise with each Navigation-Compose and Fragments in a Jetpack Compose app. You’ll study which strategy is healthier (in my view) and what obstacles could await you through the use of every of them.
Podcast!
When you’re a Polish speaker, you possibly can take heed to a podcast I’ve made about this matter:
When you Google “Jetpack Compose Navigation” you’ll most likely see the Navigation-Compose library as one of many first outcomes. Google describes it as a brand new navigation part that helps apps written in Compose and claims that “due to it we are able to navigate between composables whereas making the most of the Navigation part’s infrastructure and options.”
Sounds excellent, however is it? We should always discuss what the brand new navigation appears to be like like…
Google actually needed to create a Framework that will be capable of handle lifecycles, navigation and the whole lot round it whereas eliminating the necessity to use Fragments. They got here up with the concept to create a navigation graph as Composable (NavHost
) as an alternative of an XML file, the place we are able to outline routes to which the applying can navigate. In truth, when you look intently, Fragments are changed by navBackstackEntry on this approach. Sadly, it doesn’t work very properly and there are loads of points with it.
Initially — route paths.
Google most likely envied Flutter, React, and different comparable Frameworks and thought that the perfect resolution for Android could be to outline display routes as in an online app — although we create cellular purposes, not net ones, proper?
What does this imply for us?
Which means every route appears to be like like an URL ending, e.g.
/customers
— for the person record display/customers/2
— for the element display for person with id = 2.
Any more, the developer should keep the order of the paths and their elements, keep in mind them and their params, or create constants for particular person screens, which already implies that we now have much more work to start out than earlier than.
What comes subsequent is much more complicated.
Earlier, utilizing Fragments and Protected Args, we may outline in XML what arguments a given Fragment takes. When creating motion from one display to a different, we had an robotically generated code that requested us for arguments of the right kind and packed them into the Bundle
. Subsequent, to extract these arguments within the goal Fragment, we are able to use the by navArgs()
delegate for instance, which is able to do it for us robotically.
We have been capable of move loads of differing types as parameters, similar to Int, String, Boolean, and so on. but additionally customized ones, similar to Enums, Serializables and Parcelables. The complete record of supported varieties could be discovered here.
Is it simply as handy in navigation compose? Under no circumstances.
Arguments could be handed solely within the URL path of a given route, for instance:
- Path params —
/customers/arg1/particulars/arg2
- Or to make it much more sophisticated, you can too move question params and elective parameters after the query mark —
/customers/arg1/particulars?arg2=arg2_value&arg3=arg3_value
Oh, and don’t overlook that for every vacation spot and param that you simply wish to use, you are chargeable for the entire kind security. Compose navigation is just not going to let you know that you simply mistook an Int param as a String. It can simply crash the app.
Every param must be declared in composable
within the NavHost
:
And likewise extracted with a correct kind later:
So now, not solely that the programmer has to handle all routes himself, he additionally has to recollect what arguments are taken by a specific display and what are the keys and knowledge kinds of parameters and their order.
Will you get the errors at compile time that you simply messed one thing up? Nope. You’re going to get an error or an app crash solely whereas the app is operating.
Additionally, don’t overlook that because the display route is like an URL, every parameter should be handed anyway as a String and should be encoded.
Let’s say we wish to move this String as a param:
val urlParam = "https://translate.google.com/?hl=en&tab=TT"
To navigate with it we now have to jot down this:
navController.navigate("path/arg=$URLEncoder.encode(urlParam)")
After which obtain it like this:
URLEncoder.decode(bundle.getString("arg_key"))
Even higher, when you use URLEncoder.encode(…)
over a String that accommodates a ‘n’
character, it can crash due to the ‘%0A’
, so the one approach to make it work is to make use of Base64
encoding first.
This makes the Navigation-Compose API utterly non-type-safe.
Like I’ve talked about earlier than, any argument we wish to move, no matter kind, must be transformed to a String so as to add it to the trail.
Enums
Compose navigation gained’t allow you to move Enums
as params, however this may be executed in another way. In principle, we are able to convert it right into a String, and name the valueOf
methodology on the goal display to search out the worth within the enum that we beforehand handed as String.
Parcelables and Serializables
That is the place the enjoyable begins. Passing Serializable
and Parcelable
as a navigation param in Compose-Navigation is just not supported too.
I see lots of people on the Web saying that it’s potential to get into the backStackEntry
arguments and manually move the thing there that extends Serializable
or Parcelable
.
Nonetheless…
That is only a hack that Google doesn’t advocate and doesn’t give any assure that this may work.
Personally, I’ve had loads of conditions the place passing arguments on this approach brought on the app to crash.
Don’t imagine me? Just go here and read.
Is there something we are able to do?
Nicely, theoretically… yeah. We may convert our Serializable
or Parcelable
object to JSON and move it as a String. I really feel sorry for anybody who wish to move params this fashion. This can be a approach to do it, nevertheless it’s so silly and brings a lot pointless boilerplate and complexity that I gained’t even think about using it.
Replace
Since Compose model 1.0.3 and NavigationX 2.4.0-alpha10
we at the moment are capable of create a customized NavType
:
Let’s say you have got a category like this:
You possibly can outline the NavType
like this:
And use it:
This appears higher than what we had earlier than however nonetheless requires us to make use of JSON inside. Additionally think about creating a brand new customized NavType
each time you wish to move a Parcelable. This simply provides extra boilerplate code to our app. Furthermore, there is no such thing as a point out within the documentation from Google that this resolution will work with Compose-Navigation too.
And now the principle query arises — why?
Why is Navigation-Compose like that? Why doesn’t it assist Serializables, Parcelables and why does it have so many points?
It is rather potential that Google needs to encourage builders to move solely id of the thing and never the static copy of it. It could make sense if, for instance, we have been working on the Room database and observing the info utilizing Move. For instance, if one thing within the meantime modified this knowledge, then the person would robotically see the present values on the display.
Certainly, possibly in a number of circumstances like this, it could make sense. Sadly, there are sometimes conditions through which we wish to consciously move a static object, or at the least some a part of it in order that the person instantly sees the info on the display. After that, we may load some particulars within the background or just verify if the info is up-to-date.
Okay, then how ought to I exploit Compose to keep away from these issues? The reply is straightforward. Use Fragments and previous navigation.
Explanation why it is best to nonetheless use Fragments
You need to use no matter you need: XML, Compose, you select. The simplest approach is so as to add ComposeView
to the Fragment
view and set Composable
of the given Fragment
there.
Not a fan of LaunchedEffect
, DisposableEffect
and so forth? You don’t have to make use of them as you possibly can write some code the previous approach, identical to it was in Fragments.
Relying on what DI Framework you’re utilizing, you might also run into some points with SavedStateHandle
when you use Navigation-Compose.
In my present undertaking, we’re utilizing Koin
and sadly, SavedStateHandle
can’t be injected into the ViewModel
within the new navigation. This downside, after all, must be fastened by Koin Crew, however for now, it disappears when you use Fragments
and inject the ViewModel
utilizing by viewModel()
delegate.
After all, Fragments aren’t excellent. What points with them must you think about on this case?
Initially, you’re utilizing Fragments. This will likely appear humorous at first, however some folks don’t like them, for instance, Jake Wharton. In brief, he says you should utilize Fragments, however the backstack is a nightmare.
You may have a bit extra code. In any case, for every display, it’s important to write each Fragment and Composable.
One other factor is that you’ve got two lifecycles that you should handle — Fragment and Composable lifecycle. This will likely appear problematic at first, however thankfully, there’s a fast approach to take care of it. Simply use the setViewCompositionStrategy
methodology on a ComposeView
and set how the Composable’s lifecycle ought to behave in relation to the Fragment’s lifecycle.
Final however not least, it’s essential to keep in mind that when passing a Parcelable
object as a Protected Args argument, it’s essential to additionally add it to the proguard configuration, in any other case you’re going to get a crash on manufacturing.
So is utilizing Fragments the perfect resolution? Most likely not.
For my part, it could be finest if Google offered us with a prepared, working, and good API for navigation in Compose. Sadly, as I discussed earlier than, we are able to overlook about it for now. For the time being, the answer that I introduced is one thing that I exploit myself and I’m OK with it, at the least for now.
We must also keep in mind that many libraries and instruments don’t but have 100% assist with Compose and generally the flexibility to jot down one thing within the previous approach, in a Fragment, and even in XML is precisely what we’d like.
Thanks for studying.