10

I'm trying to show a PopupWindow from a Fragment, and then add a ComposableView to the Popup contentView, and it crashes because the ViewTreeLifecycleOwner is not found for PopupDecorView$PopupDecorView.

Things I've tried:

  • Use the PopupWindowCompat to display the popup.
  • Multiple lib versions for Fragment (1.3.4 and 1.4.0-alpha1).
  • Activity version that I'm using is: 1.3.0-alpha08.
  • Manually set the lifecycle owners for contentView and ComposeView.

Calls:

Add ComposeView to contentView:

 contentWindow.contentView.addView(
                AdaptiveCardistRender.adaptiveCardistResponseView(
                    contentWindow.contentView.context,
                    response
                )
            )

Show Popup:

PopupWindowCompat.showAsDropDown(
                popupWindow,
                anchor,
                0,
                startVerticalOffset,
                Gravity.NO_GRAVITY
            )

Set lifecycle on the PopupWindow:

class ExtensionsPopupWindow(
    val contentView: ScrollView,
    width: Int,
    height: Int,
    lifecycleOwner: LifecycleOwner,
    savedStateRegistryOwner: SavedStateRegistryOwner,
) : PopupWindow(contentView, width, height, true) {

    init {
        ViewTreeLifecycleOwner.set(contentView, lifecycleOwner)
        ViewTreeSavedStateRegistryOwner.set(contentView, savedStateRegistryOwner)
    }

Stacktrace:

    java.lang.IllegalStateException: ViewTreeLifecycleOwner not found from android.widget.PopupWindow$PopupDecorView{9dfea2f V.E...... R.....I. 0,0-0,0}
        at androidx.compose.ui.platform.WindowRecomposer_androidKt.createLifecycleAwareViewTreeRecomposer(WindowRecomposer.android.kt:242)
        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:98)
        at androidx.compose.ui.platform.WindowRecomposerPolicy.createAndInstallWindowRecomposer$ui_release(WindowRecomposer.android.kt:153)
        at androidx.compose.ui.platform.WindowRecomposer_androidKt.getWindowRecomposer(WindowRecomposer.android.kt:228)
        at androidx.compose.ui.platform.AbstractComposeView.ensureCompositionCreated(ComposeView.android.kt:200)
        at androidx.compose.ui.platform.AbstractComposeView.onAttachedToWindow(ComposeView.android.kt:235)
        at android.view.View.dispatchAttachedToWindow(View.java:20665)
        at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3493)
        at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3500)
        at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3500)
        at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3500)
        at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2544)
        at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:2057)
        at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:8501)
        at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1005)
        at android.view.Choreographer.doCallbacks(Choreographer.java:826)
        at android.view.Choreographer.doFrame(Choreographer.java:761)
        at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:990)
        at android.os.Handler.handleCallback(Handler.java:938)
        at android.os.Handler.dispatchMessage(Handler.java:99)
        at android.os.Looper.loopOnce(Looper.java:201)
        at android.os.Looper.loop(Looper.java:288)
        at android.app.ActivityThread.main(ActivityThread.java:7727)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:975)
Bruno Oliveira
  • 255
  • 1
  • 8

2 Answers2

2

By default PopupWindow cannot work with compose.

There is some setup to do for compose to find :

  • Content child
  • ViewTreeLifecycleOwner,
  • ViewTreeSavedStateRegistryOwner.

Here an example of how to show a popup from compose with a compose view inside the popup window.
(the most important part is the parentView)

val composeView = ComposeView(LocalContext.current).apply {
    setContent {
        // your composables
    }
}
val parentView = FrameLayout(LocalContext.current).apply {
    // Help compose to find View.contentChild
    id = android.R.id.content
    // Help compose to find ViewTreeLifecycleOwner
    ViewTreeLifecycleOwner.set(this, LocalLifecycleOwner.current)
    // Help compose to find ViewTreeSavedStateRegistryOwner
    ViewTreeSavedStateRegistryOwner.set(this, LocalSavedStateRegistryOwner.current)

    layoutParams = FrameLayout.LayoutParams(
        ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT
    )
    addView(composeView)

}
val popupWindow = PopupWindow(
    parentView, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT
)

popupWindow.showAtLocation(LocalView.current, Gravity.CENTER, 0, 0)

DisposableEffect(Unit) {
    onDispose {
        popupWindow.dismiss()
    }
}

If you do it from a activity replace LocalLifecycleOwner.current, LocalSavedStateRegistryOwner.current and LocalContext.current by your activity.

sagix
  • 600
  • 1
  • 4
  • 15
  • I hope it works but i've again a crash with this error : `Attempt to invoke virtual method 'android.view.View android.view.View.getRootView()' on a null object reference` – Marine Droit Nov 04 '22 at 13:19
0

I had the same issue when I tried to put AbstractComposeView in window popup as a workaround you can use compose Popup and still use the compose view as place holder inside view (it doesn't require height or width so it will not mess with your layout) then just show the popup with IntOffset where you want in the screen

Yuuuucef
  • 71
  • 2
  • 11