Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pass Parcelable argument with compose navigation

Tags:

I want to pass a parcelable object (BluetoothDevice) to a composable using compose navigation.

Passing primitive types is easy:

composable(   "profile/{userId}",   arguments = listOf(navArgument("userId") { type = NavType.StringType }) ) {...} 
navController.navigate("profile/user1234") 

But I can't pass a parcelable object in the route unless I can serialize it to a string.

composable(   "deviceDetails/{device}",   arguments = listOf(navArgument("device") { type = NavType.ParcelableType(BluetoothDevice::class.java) }) ) {...} 
val device: BluetoothDevice = ... navController.navigate("deviceDetails/$device") 

The code above obviously doesn't work because it just implicitly calls toString().

Is there a way to either serialize a Parcelable to a String so I can pass it in the route or pass the navigation argument as an object with a function other than navigate(route: String)?

like image 595
Noah Avatar asked Jan 07 '21 09:01

Noah


People also ask

Is it possible to pass parcelable objects as arguments to destinations?

In the view system navigation component, we were able to pass parcelable objects as arguments to the destinations, you might be tempted to do that in Compose Navigation as well and write up code that looks like this:

Can I Pass parcelable in the navigation composable routes?

NOTE: The Jetpack Compose team doesn’t recommend passing Parcelable in the navigation composable routes.

How do I pass arguments from one navigation route to another?

Navigation compose also supports passing arguments between composable destinations. In order to do this, you need to add argument placeholders to your route, similar to how you add arguments to a deep link when using the base navigation library: NavHost (startDestination = "profile/ {userId}") { ... composable ("profile/ {userId}") {...} }

How do I pass arguments to a route in jetpack compose?

The way it is currently done in jetpack compose is by appending the route with arguments and their respective values. For example, let’s say that we have a composable with route = “userPage”, and we want to pass arguments “userId” and “isLoggedIn”. The following snippets show how to do that in jetpack compose.


Video Answer


2 Answers

Edit: Updated to Compose Navigation 2.4.0-beta07

Seems like previous solution is not supported anymore. Now you need to create a custom NavType.

Let's say you have a class like:

@Parcelize data class Device(val id: String, val name: String) : Parcelable 

Then you need to define a NavType

class AssetParamType : NavType<Device>(isNullableAllowed = false) {     override fun get(bundle: Bundle, key: String): Device? {         return bundle.getParcelable(key)     }      override fun parseValue(value: String): Device {         return Gson().fromJson(value, Device::class.java)     }      override fun put(bundle: Bundle, key: String, value: Device) {         bundle.putParcelable(key, value)     } } 

Notice that I'm using Gson to convert the object to a JSON string. But you can use the conversor that you prefer...

Then declare your composable like this:

NavHost(...) {     composable("home") {         Home(             onClick = {                  val device = Device("1", "My device")                  val json = Uri.encode(Gson().toJson(device))                  navController.navigate("details/$json")             }         )     }     composable(         "details/{device}",         arguments = listOf(             navArgument("device") {                 type = AssetParamType()             }         )     ) {         val device = it.arguments?.getParcelable<Device>("device")         Details(device)     } } 

Original answer

Basically you can do the following:

// In the source screen... navController.currentBackStackEntry?.arguments =      Bundle().apply {         putParcelable("bt_device", device)     } navController.navigate("deviceDetails") 

And in the details screen...

val device = navController.previousBackStackEntry     ?.arguments?.getParcelable<BluetoothDevice>("bt_device") 
like image 54
nglauber Avatar answered Oct 08 '22 10:10

nglauber


I've written a small extension for the NavController.

import android.os.Bundle import androidx.core.net.toUri import androidx.navigation.*  fun NavController.navigate(     route: String,     args: Bundle,     navOptions: NavOptions? = null,     navigatorExtras: Navigator.Extras? = null ) {     val routeLink = NavDeepLinkRequest         .Builder         .fromUri(NavDestination.createRoute(route).toUri())         .build()      val deepLinkMatch = graph.matchDeepLink(routeLink)     if (deepLinkMatch != null) {         val destination = deepLinkMatch.destination         val id = destination.id         navigate(id, args, navOptions, navigatorExtras)     } else {         navigate(route, navOptions, navigatorExtras)     } } 

As you can check there are at least 16 functions "navigate" with different parameters, so it's just a converter for use

public open fun navigate(@IdRes resId: Int, args: Bundle?)  

So using this extension you can use Compose Navigation without these terrible deep link parameters for arguments at routes.

like image 35
Valentin Yuryev Avatar answered Oct 08 '22 11:10

Valentin Yuryev