Reputation: 18499
I want to open an existing fragment from compose or if i can add inside the same compose in full screen, that will also work. Also how to navigate from one existing fragment to another existing fragment from compose.
Upvotes: 17
Views: 19539
Reputation: 1774
Our recommendation for using Fragments in Compose is documented here.
Specifically, you should use the AndroidViewBinding
composable to inflate an XML containing a FragmentContainerView
hosting the fragment you want to use in Compose. AndroidViewBinding
has fragment-specific handling which is why you want to use this over AndroidView
. Note that you need to have view binding enabled for this.
Example XML file:
<androidx.fragment.app.FragmentContainerView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/fragment_container_view"
android:layout_height="match_parent"
android:layout_width="match_parent"
android:name="com.example.MyFragment" />
And then in your Composable function:
@Composable
fun FragmentInComposeExample() {
AndroidViewBinding(MyFragmentLayoutBinding::inflate) {
val myFragment = fragmentContainerView.getFragment<MyFragment>()
// ...
}
}
Upvotes: 21
Reputation: 4083
Since version 1.8.1 there is a AndroidFragment
composable similar to the AndroidView
composable to do this kind of thing.
https://developer.android.com/jetpack/androidx/releases/fragment#1.8.0
Example:
AndroidFragment(clazz = SomeExampleFragment::class.java)
As simple as the above, and you have other parameters in there to pass whatever bundle arguments you need or if you need to do something when it updates through the FragmentManager.
Here you have a few more examples:
https://github.com/search?q=AndroidFragment++language%3AKotlin&type=code
Upvotes: 7
Reputation: 1607
Document
I solved this as following step:
Theme.MaterialComponents.DayNight.NoActionBar
in res/values/themes.xml
<resources xmlns:tools="http://schemas.android.com/tools"> <style name="Theme.xxx" parent="Theme.MaterialComponents.DayNight.NoActionBar"/> </resources>
AndroidManifest.xml
<activity android:name="com.xxx" android:exported="false" android:theme="@style/Theme.xxx" />
add appcompat to your build.gradle
implementation 'androidx.appcompat:appcompat:1.6.1'
In compose
AndroidView(factory = cb@{ context->
val view = FragmentContainerView(context)
view.id = ViewCompat.generateViewId()
val selfId = view.id
supportFragmentManager.commit {
setReorderingAllowed(true)
add<MyFragment>(selfId)
}
return@cb view
})
full activity kt file
package com.xxx
import android.os.Bundle
import androidx.activity.compose.setContent
import androidx.appcompat.app.AppCompatActivity
import androidx.compose.ui.viewinterop.AndroidView
import androidx.core.view.ViewCompat
import androidx.fragment.app.FragmentContainerView
import androidx.fragment.app.commit
import androidx.fragment.app.add
import com.xxx.MyFragment
class demoFragmentActivity: AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?){
super.onCreate(savedInstanceState)
setContent{
AndroidView(factory = cb@{ context->
val view = FragmentContainerView(context)
view.id = ViewCompat.generateViewId()
val selfId = view.id
supportFragmentManager.commit {
setReorderingAllowed(true)
add<MyFragment>(selfId)
}
return@cb view
})
}
}
}
Upvotes: 6
Reputation: 580
This helped me:
@Composable
fun MyFragmentView(
fragmentManager: FragmentManager,
modifier: Modifier = Modifier
) {
AndroidView(
modifier = modifier.fillMaxSize(),
factory = { context ->
val containerId = R.id.container // some unique id
val fragmentContainerView = FragmentContainerView(context).apply {
id = containerId
}
val fragment = MyFragment()
fragmentManager.beginTransaction()
.replace(containerId, fragment, fragment.javaClass.simpleName)
.commitAllowingStateLoss()
fragmentContainerView
}
)
}
ids.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<item name="container" type="id"/>
</resources>
Upvotes: 1
Reputation: 705
I solved this as following:
@Composable
fun ReusableFragmentComponent(
someArgumentForFragment: FragmentArgument,
fragmentManager: FragmentManager,
modifier: Modifier = Modifier,
tag: String = "ReusableFragmentTag"
) {
AndroidView(
modifier = modifier,
factory = { context ->
FrameLayout(context).apply {
id = ViewCompat.generateViewId()
}
},
update = {
val fragmentAlreadyAdded = fragmentManager.findFragmentByTag(tag) != null
if (!fragmentAlreadyAdded) {
fragmentManager.commit {
add(it.id, ReusableFragment.newInstance(someArgumentForFragment), tag)
}
}
}
)
}
In my case I called this from a fragment (hosted by navigation component), in an effort to make our reusable fragments compose compatible. I did that like so:
class ReusableFragments : Fragment() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
return ComposeView(requireContext()).apply {
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
setContent {
Column {
Text("ReusableFragment 1")
ReusableFragmentComponent(someArgument1, childFragmentManager, tag = "ReusableFragmentTag1")
Text("ReusableFragment 2")
ReusableFragmentComponent(someArgument2, childFragmentManager, tag = "ReusableFragmentTag2")
}
}
}
}
}
By making the tag customizable it's possible to add multiple different instances of the same fragment to the same fragment manager.
Upvotes: 6