material-components / material-components/material-components-android
[BottomSheetDialog] `AbstractComposeView` can't be inserted a layout file used to a `BottomSheetDialog`.
Nobody has claimed this yet.
- 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
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- 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