Skip to content

Instantly share code, notes, and snippets.

@AJIEKCX
Created August 10, 2025 10:16
Show Gist options
  • Save AJIEKCX/4ba4c55ece7723fb843aaf910444ba43 to your computer and use it in GitHub Desktop.
Save AJIEKCX/4ba4c55ece7723fb843aaf910444ba43 to your computer and use it in GitHub Desktop.
This is an example of how to embed a Jetpack Compose screen into React Native with proper lifecycle handling in navigation.
import android.content.Context
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Size
import android.view.View
import android.view.ViewGroup
import android.widget.FrameLayout
import android.view.View.OnAttachStateChangeListener
import android.view.View.MeasureSpec
import kotlin.math.max
import androidx.core.view.ViewCompat
class CustomView(private val context: Context) : BaseComposeView(context) {
init {
val layoutParams = LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT
)
setLayoutParams(layoutParams)
setContent { CustomComposeContent() }
addView(composeView)
}
}
@Composable
fun CustomComposeContent() {
Canvas(modifier = Modifier.fillMaxSize()) {
val centerX = size.width / 2f
val centerY = size.height / 2f
val radius = minOf(size.width, size.height) / 4f
// Draw circle
drawCircle(
color = Color.Green,
radius = radius,
center = Offset(centerX, centerY)
)
// Draw rectangle (smaller than the circle)
val rectSize = radius * 0.7f
drawRect(
color = Color.Red,
topLeft = Offset(centerX - rectSize, centerY - rectSize),
size = Size(rectSize * 2, rectSize * 2)
)
}
}
abstract class BaseComposeView(context: Context) : FrameLayout(context) {
val composeView = ComposeView(context)
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
if (composeView.isAttachedToWindow) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
} else {
val width = max(0, MeasureSpec.getSize(widthMeasureSpec) - paddingLeft - paddingRight)
val height = max(0, MeasureSpec.getSize(heightMeasureSpec) - paddingTop - paddingBottom)
val child = composeView.getChildAt(0)
if (child == null) {
setMeasuredDimension(width, height)
return
}
child.measure(
MeasureSpec.makeMeasureSpec(width, MeasureSpec.getMode(widthMeasureSpec)),
MeasureSpec.makeMeasureSpec(height, MeasureSpec.getMode(heightMeasureSpec))
)
setMeasuredDimension(
child.measuredWidth + paddingLeft + paddingRight,
child.measuredHeight + paddingTop + paddingBottom
)
}
}
fun setContent(composable: @Composable () -> Unit) {
composeView.addOnAttachStateChangeListener(object : OnAttachStateChangeListener {
override fun onViewAttachedToWindow(v: View) {
composeView.createComposition()
val child = composeView.getChildAt(0)
if (child.measuredHeight == 0 || child.measuredWidth == 0) {
layoutComposeView()
}
}
override fun onViewDetachedFromWindow(v: View) {
composeView.disposeComposition()
}
})
composeView.setContent(composable)
}
private fun layoutComposeView() {
composeView.layout(
0,
0,
measuredWidth,
measuredHeight
)
composeView.measure(
MeasureSpec.makeMeasureSpec(measuredWidth, MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(measuredHeight, MeasureSpec.EXACTLY)
)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment