material-components / material-components/material-components-android

[BottomSheetDialog] `AbstractComposeView` can't be inserted a layout file used to a `BottomSheetDialog`.

Open
#4,207 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug
Dominant language
Java
Stars
17.4k
Forks
3.2k
PR merge metrics
No merged PRs in 30d

Description

**Description:**
- I have one `BottomSheetDialog#setContentView(R.layout.earn_more_view)`.
- Inside the layout file, I inserted `ComposeEarnMoreView` who extends from `AbstractComposeView`, and Of course, the `@Composable override fun Content()` was implemented by `Composable` functions.
- My app just crashed when I tried to pop up the dialog.

And the main log looks like below:

```
FATAL EXCEPTION: main
Process: org.alias.staging, PID: 8224
java.lang.IllegalStateException: ViewTreeLifecycleOwner not found from android.widget.FrameLayout{b3ad65d V.E...... ......I. 0,0-0,0 #7f0902db app:id/container}
at androidx.compose.ui.platform.WindowRecomposer_androidKt.createLifecycleAwareWindowRecomposer(WindowRecomposer.android.kt:352)
at androidx.compose.ui.platform.WindowRecomposer_androidKt.createLifecycleAwareWindowRecomposer$default(WindowRecomposer.android.kt:325)
at androidx.compose.ui.platform.WindowRecomposerFactory$Companion.LifecycleAware$lambda$0(WindowRecomposer.android.kt:168)
at androidx.compose.ui.platform.WindowRecomposerFactory$Companion.$r8$lambda$PmWZXv-2LDhDmANvYhil4YZYJuQ(Unknown Source:0)
at androidx.compose.ui.platform.WindowRecomposerFactory$Companion$$ExternalSyntheticLambda0.createRecomposer(Unknown Source:0)
at androidx.compose.ui.platform.WindowRecomposerPolicy.createAndInstallWindowRecomposer$ui_release(WindowRecomposer.android.kt:224)
at androidx.compose.ui.platform.WindowRecomposer_androidKt.getWindowRecomposer(WindowRecomposer.android.kt:300)
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.onAttachedToWindow(ComposeView.android.kt:283)
at android.view.View.dispatchAttachedToWindow(View.java:21748)
at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3732)
at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3739)
at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3739)
at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3739)
at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3739)
at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3739)
at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3739)
at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3739)
at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3739)
at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:3859)
at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:3146)
at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:11127)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1599)
at android.view.Choreographer.doCallbacks(Choreographer.java:1263)
at android.view.Choreographer.doFrame(Choreographer.java:1127)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:1549)
at android.os.Handler.handleCallback(Handler.java:966)
at android.os.Handler.dispatchMessage(Handler.java:110)
at android.os.Looper.loopOnce(Looper.java:205)
at android.os.Looper.loop(Looper.java:293)
at android.app.ActivityThread.loopProcess(ActivityThread.java:9986)
at android.app.ActivityThread.main(ActivityThread.java:9975)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:586)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1240)
```

**Expected behavior:** My app won't crash and the layout file should be shown in the `BottomSheetDailog`.

**Source code:** `BottomSheetDialog` or its parent `AppCompatDialog` or its delegate `AppCompatDelegateImpl` has no LifecycleOwner.

If we check the source code of `androidx.activity.ComponentDialog`, which implements `LifecycleOwner` and called`window!!.decorView.setViewTreeLifecycleOwner(this)` when `add/setContentView`.

But `BottomSheetDialog/AppCompatDialog/AppCompatDelegateImpl` didn't.

**Minimal sample app repro:**

```
class CrashBottomSheetDialog(context: Context) : BottomSheetDialog(context) {
init {
setContentView(object : AbstractComposeView(context) {
@Composable
override fun Content() {
Text(text = "Hello, World")
}
})
}
}
```

**Android API version:** Android 10

**Material Library version:** `com.google.android.material:material:1.9.0~1.12.0`

**Device:** Huawei Mate 50

**Solution:**: I found this issue around May, 2023. I thought google team would fix this soon but actually they didn't. So I create an Issue for it to see if I could push google team to fix it inside the material library.

I had a solution like below, hoping it could inspire you guys somehow. Also I could create a PR like the solution below or create a PR inside `AppCompatDelegateImpl`. Tell me what you think.

```
class ComponentBottomSheetDialog @JvmOverloads constructor(
context: Context,
@StyleRes themeResId: Int = 0
) : BottomSheetDialog(context, themeResId),
LifecycleOwner,
SavedStateRegistryOwner {

private var _lifecycleRegistry: LifecycleRegistry? = null
private val lifecycleRegistry: LifecycleRegistry
get() = _lifecycleRegistry ?: LifecycleRegistry(this).also {
_lifecycleRegistry = it
}

private val savedStateRegistryController: SavedStateRegistryController =
SavedStateRegistryController.create(this)
override val savedStateRegistry: SavedStateRegistry
get() = savedStateRegistryController.savedStateRegistry

override val lifecycle: Lifecycle
get() = lifecycleRegistry

override fun onSaveInstanceState(): Bundle {
val bundle = super.onSaveInstanceState()
savedStateRegistryController.performSave(bundle)
return bundle
}

@Suppress("ClassVerificationFailure") // needed for onBackInvokedDispatcher call
@CallSuper
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
savedStateRegistryController.performRestore(savedInstanceState)
lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_CREATE)
}

@CallSuper
override fun onStart() {
super.onStart()
lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_RESUME)
}

@CallSuper
override fun onStop() {
// This is the closest thing to onDestroy that a Dialog has
lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY)
_lifecycleRegistry = null
super.onStop()
}

override fun setContentView(layoutResID: Int) {
initViewTreeOwners()
super.setContentView(layoutResID)
}

override fun setContentView(view: View) {
initViewTreeOwners()
super.setContentView(view)
}

override fun setContentView(view: View, params: ViewGroup.LayoutParams?) {
initViewTreeOwners()
super.setContentView(view, params)
}

override fun addContentView(view: View, params: ViewGroup.LayoutParams?) {
initViewTreeOwners()
super.addContentView(view, params)
}

private fun initViewTreeOwners() {
window!!.decorView.setViewTreeLifecycleOwner(this)
window!!.decorView.setViewTreeSavedStateRegistryOwner(this)
}
}
```

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with BottomSheetDialog, AppCompatDialog, and AppCompatDelegateImpl, then compare their content-view setup with androidx.activity.ComponentDialog and its view-tree owner handling. Reproduce the failure with the provided CrashBottomSheetDialog sample; done means an AbstractComposeView can be shown in a BottomSheetDialog without the ViewTreeLifecycleOwner crash.

Written by the indexing model from the issue text.

Assessment

Tech stack
kotlin
Domain
mobile
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.