Fragment Container in Jetpack Compose
Asked Answered
B

4

19

I want to develop a single Activity multi fragments Apps using Jetpack Compose. For recyclerView, we have Vertical and HorizontalScroller. But, for fragments what should I use.

fun loadFragment(fragment: Fragment) {
            val transaction:FragmentTransaction = supportFragmentManager.beginTransaction()
            transaction.replace(R.id.f_container, fragment)
            transaction.addToBackStack(null)
            transaction.commit()
        }

In this case, I don't have R.id.f_container because I'm creating UI using only compose.

<FrameLayout
    android:id="@+id/f_container"
    android:layout_width="match_parent"
    android:layout_height="match_parent"`enter code here`
    app:layout_behavior="@string/appbar_scrolling_view_behavior"
    app:layout_constraintEnd_toEndOf="parent"
    tools:layout_editor_absoluteY="-56dp">
</FrameLayout>
Bluhm answered 4/3, 2020 at 6:22 Comment(3)
Fragments need a ViewGroup container. That is unlikely to change. So, if you want to continue using fragments, you will need a ViewGroup in the appropriate place. Right now, with Compose/view interoperability very much a "work in progress", that probably means that your fragment will need to go into a traditional layout.Thigh
@Thaw De Zin if you have found a solution for your question please let me knowCostanzo
what about using AndroidViewBinding that loads a navhost fragment (with nav graph possibly), then with navigation component you basically can also navigate through fragments that renders compose in this scenario developer.android.com/jetpack/compose/migrate/…Capitation
P
13

You can create your own @Composable FragmentContainer function

@Composable
fun FragmentContainer(
    modifier: Modifier = Modifier,
    fragmentManager: FragmentManager,
    commit: FragmentTransaction.(containerId: Int) -> Unit
) {
    val containerId by rememberSaveable { mutableStateOf(View.generateViewId()) }
    var initialized by rememberSaveable { mutableStateOf(false) }
    AndroidView(
        modifier = modifier,
        factory = { context ->
            FragmentContainerView(context)
                .apply { id = containerId }
        },
        update = { view ->
            if (!initialized) {
                fragmentManager.commit { commit(view.id) }
                initialized = true
            } else {
                fragmentManager.onContainerAvailable(view)
            }
        }
    )
}

/** Access to package-private method in FragmentManager through reflection */
private fun FragmentManager.onContainerAvailable(view: FragmentContainerView) {
    val method = FragmentManager::class.java.getDeclaredMethod(
        "onContainerAvailable",
        FragmentContainerView::class.java
    )
    method.isAccessible = true
    method.invoke(this, view)
}

and then use it in activity

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContent {
        FragmentContainer(
            modifier = Modifier.fillMaxSize(),
            fragmentManager = supportFragmentManager,
            commit = { add(it, YourFragment()) }
        )
    }
}

or in fragment

override fun onCreateView(
    inflater: LayoutInflater,
    container: ViewGroup?,
    savedInstanceState: Bundle?
): View = ComposeView(requireContext()).apply {
    setContent {
        FragmentContainer(
            modifier = Modifier.fillMaxSize(),
            fragmentManager = parentFragmentManager,
            commit = { add(it, YourNestedFragment()) }
        )
    }
}
Psychologism answered 22/1, 2022 at 22:51 Comment(1)
This requires Fragment KTX library, e.g. implementation "androidx.fragment:fragment-ktx:1.4.1"Glob
F
4

I would not suggest you reuse an existing fragment, but instead, try to recycle its inner views to create a new composable.

Anyway if you want to have fun here is a possible solution:

AndroidView(
modifier = Modifier
    .fillMaxHeight()
    .fillMaxWidth(),
factory = { context ->
    FrameLayout(context).apply {
        FragmentBlankBinding.inflate(LayoutInflater.from(context), this, true).root
    }
})

Enjoy!

Fortin answered 20/10, 2021 at 13:13 Comment(0)
J
0

Probably late for the answer, just leaving it so others find it useful.

Since version 1.8.1, the Android development framework includes an AndroidFragment composable, which facilitates the integration of Fragments into Compose.

For more information, visit the AndroidFragment Release Notes.

Here is an example of how to use AndroidFragment:

@Composable
fun ExampleInteropScreen() {
   AndroidFragment<ExampleFragment>(
        modifier = Modifier
            .fillMaxSize()
   )
}

This composable allows developers to seamlessly use Fragments within a Compose UI, enhancing interoperability and expanding the capabilities of modern Android applications.

Joly answered 17/7, 2024 at 18:26 Comment(0)
L
-2

If you are creating a new app, you can skip using Fragments altogether and just use composable functions to represent your screens.

Lozoya answered 24/8, 2020 at 3:14 Comment(1)
+1 - I don't understand the negative votes under that comment. Fragments are just containers, they can be replaced with any other containers. It may turn out that best practices will change in favor of what is stated in this comment.Fascine

© 2022 - 2025 — McMap. All rights reserved.