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.