Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using a ComposeView inside layout or a BottomSheetDialog throws exception

I have a non compose view that needs to show a BottomSheetDialog. I would like the root view:

myBottomSheetDialog = BottomSheetDialog(requireActivity(), R.style.Theme_Design_BottomSheetDialog)
val bottomSheetView = BottomsheetBinding.inflate(layoutInflater, null, false)
myBottomSheetDialog.setContentView(bottomSheetView.root)

Where the BottomSheetBinding view is:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

        <androidx.compose.ui.platform.ComposeView
            android:id="@+id/bottom_sheet_compose"
            android:layout_width="match_parent"
            android:layout_height="wrap_content">

        </androidx.compose.ui.platform.ComposeView>

</RelativeLayout>

When I open the dialog the following exception is thrown.

java.lang.IllegalStateException: ViewTreeLifecycleOwner not found from android.widget.FrameLayout{ee8d547 V.E...... ......I. 0,0-0,0 #7f0a00c1 app:id/container}
    at androidx.compose.ui.platform.WindowRecomposer_androidKt.createLifecycleAwareViewTreeRecomposer(WindowRecomposer.android.kt:244)
    at androidx.compose.ui.platform.WindowRecomposer_androidKt.access$createLifecycleAwareViewTreeRecomposer(WindowRecomposer.android.kt:1)
    at androidx.compose.ui.platform.WindowRecomposerFactory$Companion$LifecycleAware$1.createRecomposer(WindowRecomposer.android.kt:99)
    at androidx.compose.ui.platform.WindowRecomposerPolicy.createAndInstallWindowRecomposer$ui_release(WindowRecomposer.android.kt:155)
    at androidx.compose.ui.platform.WindowRecomposer_androidKt.getWindowRecomposer(WindowRecomposer.android.kt:230)
    at androidx.compose.ui.platform.AbstractComposeView.resolveParentCompositionContext(ComposeView.android.kt:244)
    at androidx.compose.ui.platform.AbstractComposeView.ensureCompositionCreated(ComposeView.android.kt:251)
    at androidx.compose.ui.platform.AbstractComposeView.onMeasure(ComposeView.android.kt:288

For a few reasons I cannot make the parent view compose first. But I would like to show a dialog and use compose for the content within that dialog.

like image 318
lostintranslation Avatar asked Aug 04 '26 22:08

lostintranslation


1 Answers

In order to use Composables as content of any window we must set view tree owners on the root of the view tree. In most of the android main classes it's done by google devs already, but for some reason they forgot about BottomSheetDialog.

Here is the solution, call setViewTreeOwners() before calling BottomSheetDialog.setContentView()

private fun setViewTreeOwners() {
    val activity = extractActivity(context) // if you have activity present nearby, then just use your activity here.
    val decorView = window?.decorView
    if (activity != null && decorView != null) {
        ViewTreeLifecycleOwner.set(decorView, activity as? LifecycleOwner)
        ViewTreeViewModelStoreOwner.set(decorView, activity as? ViewModelStoreOwner)
        ViewTreeSavedStateRegistryOwner.set(decorView, activity as? SavedStateRegistryOwner)
    }
}

Optional code for extracting activity from context

private fun extractActivity(context: Context?): Activity? {
    var currentContext = context
    while (currentContext != null) {
        if (currentContext is Activity) return currentContext
        if (currentContext !is ContextWrapper) break
        currentContext = currentContext.baseContext
    }
    return null
}
like image 51
Константин Семочкин Avatar answered Aug 07 '26 19:08

Константин Семочкин



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!