Marcello Galhardo
08/04/2021, 1:46 PMNavDestination.createRoute
to use Parcelable
with Navigation Component when using the new Route System (String based) for Fragments/Composables? I understand that it is an internal API, and I’m not supposed to use it, so I’m coming here to find a better option before making a final decision (see the thread for more details).
I appreciate any help you can provide.Marcello Galhardo
08/04/2021, 1:46 PMParcelable
with the Route System, but in my current scenario, I have 3 options:
1. To ignore their opinion and find a way to use Parcelable
(cheaper option);
2. Give up on using Navigation Component, and write a custom navigation system;
3. Rewrite all my navigation logic in the entire application (most expensive option).
The overall context is that I have a huge multi-activity application. There, Parcelable
is used to define a NavigationKey
to navigate an Activity
in the multi-module structure in a safe way.
My goal is to support Single Activity; and not to rewrite all routes, argument decoding, etc... the Single Activity is already a big work, having to care about that too is simple too much.
Therefore, we came up with a hack solution to reuse what we already have:
// Method exposing `createRoute`.
fun NavController.navigate(
route: String,
args: Bundle?,
navOptions: NavOptions? = null,
navigatorExtras: Navigator.Extras? = null
) = navigate(NavDestination.createRoute(route).hashCode(), args, navOptions, navigatorExtras)
// Key contract - could be better, just for example.
abstract class NavigationKey : Parcelable {
val route: String get() = requireNotNull(this::class.qualifiedName)
companion object {
const val TAG: String = "NAVIGATION_KEY"
}
}
// A destination key
@Parcelize
data class HomeKey(
val id: String,
// other attrs.
) : NavigationKey()
// How to use it.
fun example() {
val navController: NavController // ...
// Navigating:
val key = HomeKey(id = "123")
navController.navigate(route = key.route, args = bundleOf(NavigationKey.TAG to HomeKey(id="123")))
// Reading arguments (from a Fragment):
val key = requireArguments().getParcelable<HomeKey>(NavigationKey.TAG)
}
EDIT: Ah, and here goes createRoute
implementation:
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public fun createRoute(route: String?): String =
if (route != null) "<android-app://androidx.navigation/$route>" else ""
That works. But I feel really bad about doing it. If anyone has other ideas, I would be glad to hear. Thank you in advance!Ian Lake
08/04/2021, 5:08 PMMarcello Galhardo
08/04/2021, 7:34 PM// create a key that will be used to navigate.
@Parcelize
interface HomeKey(
val id: String,
// ... other attrs.
) : NavigationKey
// navigates
fun onClickButton() {
val key = HomeKey(id = "123")
val intent = intentFactory.createIntent(key) // Maps a key to an intent factory, which will know what activity is targetting, and add the key to the args bundle.
startActivity(intent)
}
// read the key from within an Activity/Fragment
val arg by lazy {
requireArguments().getKey<HomeKey>() // in practice, `getParcelable<HomeKey>("NAVIGATION_KEY")`
}
Now, with the “hack solution” using Parcelable
from above, we managed to get the following result for navigation (without touch the key or how to read a key). The following code "works" in our codebase using the hack and allow us to reuse a lot of code:
// navigates
fun onClickButton() {
val key = HomeKey(id = "123")
findNavController().navigate(key)
}
It is true we would still need to change many things (well, we need to move from Activity
to Fragment
, and hopefully to Compose at some point) - but be able to use route + Parcelable
allow us to keep an abstraction very similar to what we have today to reduce the quantity of changes we need to do at once. However, we rely on createRoute
which is far from ideal as it is an internal API (the only way we found to use Parcelable
with string routes).
By the way, thanks for replying me (again) and helping with Navigation Component (and sorry for the long message). 🙏Ian Lake
08/04/2021, 11:36 PMMarcello Galhardo
08/05/2021, 6:51 AMnavigation-keys
module that contain most keys. Alternatively, a feature (e.g., search
) can define an -api
module (e.g., search-api
) that have their public API, which may include keys. And the IntentFactory
is a simple Dagger’s Multi-Binding that receives, in summary, Map<NavigationKey, (NavigationKey -> Intent)>
and wire everything together.Marcello Galhardo
08/05/2021, 7:51 AMParcelable
(years ago) and now, route: String
does not allow us to use Parcelable
- which makes it really hard for us to reuse what we have.