Jetpack Composable ๐ to Bitmap Image ๐
How to convert a jetpack composable to the bitmap image by not going insane jumping between the interpolation.
Featured on :
Mobile App Circular

Hacktoberfest 2021 is live and many developers like me who are enthusiastic about this event and love Open Source take some time to contribute to some Open Source Projects.
My 3rd PR is on Shreyas Patilโs NotyKT repository Issue number 119 :
That is to add support to share composable into Bitmap, so the app could share Notes in Image format as well.
The solution for this problem is already there on StackOverflow and Medium. Quick google with get you those, I also went over them to understand how to provide this feature, but there was a lot of complexity and Manual hardCoded height, width, Canvas hacks, and Ugly ass API design for Creating View to capture bitmap was there.
Providing this Complexity but a huge no-no for me but the way they solve seems to be the solution for now. Mostly solution required you to convert Composable โ View, View โ Bitmap, and there was a lot of passing and multiple composable calling in the solution.
For which I decided to clean up and use my small brain ๐ง to reduce those repeated work and clean up the API design.
How to Convert Composable โ View??
Simple we can use ComposeView
which would accept composable as its View.
// Native View which accepts Composable as its View Content
ComposeView(context).apply {
setContent {
// composable goes here!
}
}
We can Create Compose View Item at call site programatically. Since our app would be in Compose It would be great if we could use this inside of Composable.
How to Covert Native View โ Composable ??
We can use AndroidView
to wrap our Compose View then externally pass it the composable context to apply on ComposeView.
@Composable
fun CaptureBitmap(...){
// we will use compose view as target to get bitmap
val composeView = ComposeView(...)
// put compose native view to compose ui using AndroidView
AndroidView(
factory = {
composeView.apply {
setContent {
.... put compose here
}
}
}
)
}
Enjoying the Post?
a clap is much appreciated if you enjoyed. No sign up or cost associated :)
If you want to support my content do consider dropping a tip/coffee/donation๐ฐ
We will get the compose content body from outside so it could be re-used as a scaffold for all the places that need to be screenshot.
How to create Scaffolding Composables for Template Like Reusing?
@Composable
fun CaptureBitmap(
content: @Composable ()->Unit // <--- we use callback as parameter
){
val composeView = ComposeView(...)
AndroidView(
factory = {
composeView.apply {
setContent {
content.invoke() // <--- Content get places in between this code
}
}
}
)
}
We have created the logic to get our composable content wrapped inside of a native view which we can use to get bitmap, BUT : this capturing canโt be done instantly cause when this code will run it will take some time to inflate this view.
We can have multiple ways to solve this waiting time, from observing view tree to writing a state which callbacks when view is rendered. This will make to capture your view bitmap only once the layout is over.
But what might happen that you composable UI might get updated many time cause of from state changes. This will ask you to recapture bitmap every time the UI changes and cache it to get the latest value. This flow is very complex to deal with and code.
Ideally, we should be able to get bitmap out for some sharing action is getting performed on the UI so we can get the latest value from the state itself.
I solved this by using a callback. Ideally composable function holding UI thus should not return a value but this is an exceptional case I guess where we need action-based returned value.
How to create a callback to get latest bitmap?
@Composable
fun CaptureBitmap(
content: @Composable ()->Unit
) : () -> Bitmap // <---- this will return a callback which returns a bitmap
{
val composeView = ComposeView(...)
//callback that convert view to bitmap
fun captureBitmap() = composeView.drawToBitmap()
AndroidView(
factory = {
composeView.apply {
setContent {
content.invoke() // <--- Content get places in between this code
}
}
}
)
return ::captureBitmap // <--- return functional reference of the callback
}
captureBitmap()
will be returned from function which would be a callback that would trigger ComposeView to return a bitmap of the View.
Final Composable Would look like?
@Composable
fun CaptureBitmap(
content: @Composable () -> Unit,
): () -> Bitmap {
val context = LocalContext.current
/**
* ComposeView that would take composable as its content
* Kept in remember so recomposition doesn't re-initialize it
**/
val composeView = remember { ComposeView(context) }
/**
* Callback function which could get latest image bitmap
**/
fun captureBitmap(): Bitmap {
return composeView.drawToBitmap()
}
/** Use Native View inside Composable **/
AndroidView(
factory = {
composeView.apply {
setContent {
content.invoke()
}
}
}
)
/** returning callback to bitmap **/
return ::captureBitmap
}
How to use composable at the call-site?
val snapShot = CaptureBitmap {
//.. wite composable you want to capture
// it would be visible on view as well
}
// Caution : needs to be done on click action
// ui must be visible/laid out before calling this
val bitmap = snapShot.invoke()
when snapShot.invoke()
it called, it will trigger the callback and would get the latest bitmap value. Check out examples to understand the use case.
Example :
Coloumn {
val snapShot = CaptureBitmap {
Button(
onClick = {},
modifier = Modifier
.fillMaxWidth()
.padding(24.dp)
.height(55.dp),
) {
Text(text = "Capture my image")
}
}
Button(
onClick = {
MainScope().launch {
val bitmap = snapShot.invoke()
val uri = saveImage(bitmap, context)
if (uri != null) {
shareImageUri(context, uri)
}
}
},
modifier = Modifier
.fillMaxWidth()
.padding(24.dp)
.height(55.dp),
) {
Text(text = "Share")
}
}
Checkout full Implementation at :
If you guys have suggestions that could help me implement a better API Iโm all ears reach me out on my social media.
Update Dec 2021 :
Thanks @Shreyas Patil to come up with way to convert the method to covert it to callback/event out style api
@Composable
fun CaptureBitmap(
captureRequestKey: String,
content: @Composable () -> Unit,
onBitmapCaptured : (Bitmap) -> Unit
) {
val context = LocalContext.current
/**
* ComposeView that would take composable as its content
* Kept in remember so recomposition doesn't re-initialize it
**/
val composeView = remember { ComposeView(context) }
// If key is changed it means it's requested to capture a Bitmap
LaunchedEffect(captureRequestKey) {
view.post {
onBitmapCaptured.invoke(view.drawToBitmap())
}
}
/** Use Native View inside Composable **/
AndroidView(
factory = {
composeView.apply {
setContent {
content.invoke()
}
}
}
)
}
check out more on :
If you like my content and want to see more, click right here to see more ๐

Until next time, Happy Hacking! ๐จ๐ปโ๐ป
Enjoying the Post?
a clap is much appreciated if you enjoyed. No sign up or cost associated :)
If you want to support my content do consider dropping a tip/coffee/donation๐ฐ
Reach me out :
