oiyio
oiyio

Reputation: 5925

Showing custom dialog made with ComposeView inside Xml based activity - Jetpack Compose - Android

In my activity class whose layout set with xml, i want to show a dialog. ın this dialog i want to use Jetpack Compsose. Is this possible ? My codes are as the following and i get the following crash :

java.lang.IllegalStateException: ViewTreeLifecycleOwner not found from android.widget.RelativeLayout{910e78c V.E...... ......I. 0,0-0,0}

Here is my activity class :

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val button = findViewById<android.widget.Button>(R.id.buttonPuppyInfo)
        button.text = "Open dialog"

        val dialog =  Dialog(this@MainActivity)
        dialog.setContentView(R.layout.custom_dialog);
        val composeView = dialog.findViewById<ComposeView>(R.id.composeView)
        composeView.setContent {
            Button(
                onClick = {
                    // Change the state to close the dialog
                    //setShowDialog(false)
                },
            ) {
                Text("Superrr")
            }
        }

        button.setOnClickListener {
            dialog.show();
        }

    }

}

Below is my custom_dialog.xml

<?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">

    <TextView
        android:id="@+id/textView"
        android:text="hello world!"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>


    <androidx.compose.ui.platform.ComposeView
        android:id="@+id/composeView"
        android:layout_height="match_parent"
        android:layout_width="match_parent"
        android:layout_below="@id/textView">
    </androidx.compose.ui.platform.ComposeView>


</RelativeLayout>

Below is my dependencies const val composeVersion = "1.0.2"

implementation("androidx.compose.ui:ui:${Versions.composeVersion}")
implementation("androidx.compose.ui:ui-tooling:${Versions.composeVersion}")
implementation("androidx.compose.material:material:${Versions.composeVersion}")
implementation("androidx.compose.material:material-icons-extended:${Versions.composeVersion}")
implementation("androidx.compose.runtime:runtime:${Versions.composeVersion}")

implementation("androidx.activity:activity-compose:1.3.1")

implementation("androidx.navigation:navigation-compose:2.4.0-alpha08")

implementation("org.jetbrains.kotlin:kotlin-stdlib:${Versions.kotlinVersion}")
implementation("androidx.core:core-ktx:1.6.0")
implementation("androidx.appcompat:appcompat:1.4.0-alpha03")
implementation("com.google.android.material:material:1.4.0")

Upvotes: 12

Views: 5066

Answers (3)

DTechnlogy
DTechnlogy

Reputation: 345

Same question as https://stackoverflow.com/posts/77523002/edit

Currently AppCompatDialog which extends ComponentDialog had a LifeCycleOwner bug because it didn't forward setContent call to it's super, causing LifeCycleOwner missing https://issuetracker.google.com/issues/261314581

You can inject it yourself by doing this before setConentView()

fun injectViewTree() {
  try {
    window!!.decorView.setViewTreeLifecycleOwner(this)
    window!!.decorView.setViewTreeOnBackPressedDispatcherOwner(this)
    window!!.decorView.setViewTreeSavedStateRegistryOwner(ownerActivity as ComponentActivity)
    window!!.decorView.setViewTreeViewModelStoreOwner(ownerActivity as ComponentActivity)
  } catch (e: Exception) {
    Log.e("BaseMvpDialogExt", "injectViewTree", e)
  }
}

Then call in your custom dialog, call it before setContent

    injectViewTree(this);
    setContentView(viewDataBinding.getRoot());

Upvotes: -1

oiyio
oiyio

Reputation: 5925

After thinking more, I believed that this idea is bad, so i didn't waste time on this unnecessary and wrong idea. If your activity is xml based do your dialog with xml. If it's based on compose than do it with compose. That's my inference.

Upvotes: -1

Omar Abdan
Omar Abdan

Reputation: 1975

I'm not sure about using Compose with the old android UI Dialog, I think you should use compositionStrategy which tells compose to bound to an existing lifecycle.

setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)

        

Also what I did in my current app is using Compose Dialog with the old activity/fragment XML.

Fragment/Activity XML:

<FrameLayout>

// here my activity/fragment xml ui
<Constraintlayout/>

// here I will call the Compose Dialog 
<androidx.compose.ui.platform.ComposeView
    android:id="@+id/compose_view"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

</FrameLayout>

In the Activity/Fragment:

composeView.apply {
            setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
            setContent {
                var openDialog by remember { mutableStateOf(true) }

                Theme {
                   if(openDialog){
                    AlertDialog(...)
                   }
                }
            }
        }

Upvotes: 3

Related Questions