3

I'm using jetpack compose. I have two screens, and I want to send a Bitmap from the first one to the second one. So I convert my Bitmap to string and pass it as an argument:


                            composable(
                                route = "${NavGraph.FilterScreen.route}/{screenShot}",
                                arguments = listOf(navArgument("screenShot") {
                                    this.type = NavType.StringType
                                })
                            ) {
                                FilterScreen(
                                    innerPadding = padding,
                                    navController = navController,
                                    screenShot = it.arguments?.getString("screenShot")
                                )
                            }

and I navigate from the first screen to the second one like this:

 navController.navigate(NavGraph.FilterScreen.route + "/${bitmapToString(it)}")

the problem is:

it seems because the string version of Bitmap is so long, the navigation can't handle it and gives the following error:

cannot be found in the navigation graph NavGraph(0x0) startDestination={Destination(0x78d845ec) route=home}

I'm saying that because everything worked when I replaced a small random string with a string containing Bitmap values.

I also tried to use parcellable. But I get the error that parcellable can not have a default value, so we must pass as a string. How can I solve this?

Mohammad Derakhshan
  • 1,262
  • 11
  • 33
  • Does this answer your question? [How to pass an image from one composable function to another in Jetpack Compose?](https://stackoverflow.com/questions/71974049/how-to-pass-an-image-from-one-composable-function-to-another-in-jetpack-compose) – nglauber May 07 '22 at 13:50
  • Also this question can help you... https://stackoverflow.com/questions/72122868/passing-uri-between-compose-screens-causes-securityexception-permission-denial/72153025#72153025 – nglauber May 07 '22 at 13:52
  • @nglauber I think No. I have a bitmap and want to pass the bitmap. – Mohammad Derakhshan May 07 '22 at 14:15
  • 3
    Passing a Bitmap as part of your saved instance state (which all arguments are) will never work: you'll get `TransactionTooLargeException` almost immediately. – ianhanniballake May 07 '22 at 15:14
  • 1
    Just store the model in a single viewModel and access that from there within both the screens. ViewModels are essentially sources of truth so this is the correct approach. Little bit of state-hoisting and you're good. If you're unclear about state-hoisting, read [here](https://stackoverflow.com/a/71817269/15880865) – Richard Onslow Roper May 07 '22 at 18:24
  • Store the bitmap* in a single ViewModel – Richard Onslow Roper May 08 '22 at 03:37
  • But my screens operate very different things. So I have a view model for each. Are you suggesting merging them? @MARSK – Mohammad Derakhshan May 08 '22 at 08:36
  • There's no problem in merging the ViewModels per my opinion, and even if you do not wish to do that, you could just create another, common ViewModel for both the screens to do tasks like data-sharing. It's fine. – Richard Onslow Roper May 08 '22 at 10:49
  • @MARSK Having a common for data-sharing seems reasonable to me also. Thank you. – Mohammad Derakhshan May 08 '22 at 12:09
  • @ianhanniballake Ok, so how can we share a large amount of data between screens regarding not being able to use parcelabel classes. ( and not willing to use data storage, i.e, storing the data using the room and retrieving it on another screen) – Mohammad Derakhshan May 08 '22 at 12:14

1 Answers1

0

You can save the image in a temporary file on the first screen and then read that image on the second screen and after that, you can delete that file. I make a sample project on Github, it's maybe not the best code for it but you get the idea

first screen:

 val context = LocalContext.current
 val path = context.getExternalFilesDir(null)!!.absolutePath

 val image = YOUR_BITMAP_IMAGE

 val tempFile = File(path , "tempFileName.jpg")
 val fOut = FileOutputStream(tempFile)
 image.compress(Bitmap.CompressFormat.JPEG , 100 , fOut)
 fOut.close()

 goToNextScreen()

second screen:

val context = LocalContext.current

val path = context.getExternalFilesDir(null)!!.absolutePath
val imagePath = "$path/tempFileName.jpg"

val image = BitmapFactory.decodeFile(imagePath)
File(imagePath).deleteOnExit() // Delete temp image

It may not be the best solution but it will do the job.

  • 1
    Thank you. But it takes a while since it should generate the file and then retrieve it. The picture comes from a screenshot, so it is a heavy procedure. I'm not sure using this approach doesn't cause any performance reduction. – Mohammad Derakhshan May 08 '22 at 08:39