ProAndroidDev

The latest posts from Android Professionals and Google Developer Experts.

Follow publication

Fragments & Compose interoperability — Android

This article aims to provide an overview of the different APIs available for developers to integrate composables in Fragments and vice versa.

We will cover the following topics

  • Composable in XML-based Fragment
  • Full content of the fragment as Composable
  • Using Fragment in Compose-based UI — AndroidViewBinding
  • Using Fragment in Compose-based UI — AndroidViewFragment
  • Using Composables directly with Navigation library

Composable in XML-based Fragment

  • Layout file
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/coordinator_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">



<androidx.compose.ui.platform.ComposeView
android:id="@+id/hello_compose_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>


</androidx.coordinatorlayout.widget.CoordinatorLayout>
  • Accessing ComposeView from Kotlin-based Activity or Fragment
class FragmentWithComposable : Fragment() {


override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
)
: View {
val rootView: View = inflater.inflate(R.layout.fragment_with_composable, container, false)

rootView.findViewById<ComposeView>(R.id.hello_compose_view).apply {
setContent {
YourAppTheme {
Text("Hello from Composable")
}
}
}
return rootView
}
}

You can also use viewBinding instead of findViewById

Multiple composables

  • When we want to use multiple composables, we must add an ID to each composable.
  • Define IDs in res/values/ids.xml file
<?xml version="1.0" encoding="utf-8"?>

<resources>
<item name="compose_view_1" type="id" />
<item name="compose_view_1" type="id" />
</resources>
class ExFragmentMultipleComposeView : Fragment() {

override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
)
: View = LinearLayout(requireContext()).apply {
addView(
ComposeView(requireContext()).apply {
setViewCompositionStrategy(
ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed
)
id = R.id.compose_view_1
// ...
}
)
addView(TextView(requireContext()))
addView(
ComposeView(requireContext()).apply {
setViewCompositionStrategy(
ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed
)
id = R.id.compose_view_1
// ...
}
)
}
}

Fragment with full Compose based UI — ComposeView

  1. Manually set the view composition strategy and then set the content
class BlankFragment : Fragment() {

var uri: Uri? = null

override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
)
: View {
return ComposeView(requireContext()).apply {
// Dispose of the Composition when the view's LifecycleOwner is destroyed
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
setContent {
MaterialTheme {
// In Compose world
HomeScreen()
}
}
}
}

}

2. Fragment — content extension function

It's available starting with version 1.7.0 of fragment via fragment-compose artifact

Add dependency to your project. Here is an example of using the Version catalog

fragmentKtx = "1.8.4"
androidx-fragment-compose = { module = "androidx.fragment:fragment-compose", version.ref = "fragmentKtx" }
  • In your module’s build.gradle file
implementation(libs.androidx.fragment.compose)
  • Code of content function
fun Fragment.content(content: @Composable () -> Unit): ComposeView {
return ComposeView(requireContext()).apply {
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
setContent(content)
}
}

As you can see it handles the setting of view composition strategy and it takes content as Composable and call setContent internally for us.

class FragmentWithContentExt : Fragment() {

override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
)
: View = content {
Column(modifier = Modifier
.fillMaxSize()
.systemBarsPadding()) {
Text("Hello World")
}
}
}

Fragment in Compose-based UI — AndroidViewBinding

  • Fragment code
class BlankFragment : Fragment() {

var uri: Uri? = null

override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
)
: View {
return ComposeView(requireContext()).apply {
// Dispose of the Composition when the view's LifecycleOwner is destroyed
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
setContent {
MaterialTheme {
// In Compose world
HomeScreen()
}
}
}
}

}
  • Layout file — FragmentContainerView
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">


<androidx.fragment.app.FragmentContainerView
android:id="@+id/fragment_container_view"
android:name="com.example.compose.BlankFragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />


</androidx.constraintlayout.widget.ConstraintLayout>
  • Composable AndroidViewBinding code
@Composable
fun FragmentUsingViewBindingInCompose(modifier: Modifier = Modifier) {
AndroidViewBinding(FragmentContainerBinding::inflate, modifier){
val blankFragment = fragmentContainerView.getFragment<BlankFragment>()
blankFragment.uri = Uri.parse("https://developer.android.com")
}
}

Fragment in compose-based UI — AndroidFragment

AndroidFragment has been available since version 1.8.0 via fragment-compose artifact

  • Composable code
@Composable
fun FragmentUsingAndroidFragmentInCompose(modifier: Modifier = Modifier) {
AndroidFragment<BlankFragment> { blankFragment ->
blankFragment.uri = Uri.parse("https://developer.android.com")
}
}

Navigation Fragment Composable

Starting with version 2.8.0 we get support for adding composable directly into XML-based nav-graphs via navigation-fragment-compose

2 important steps that must be implemented to use this API

  1. Each composable destination must be a top-level, no argument @Composable method
  2. A fully qualified name is used as the android:name attribute for each destination

💡Tip: for the file name you can use @file:JvmName annotation for better/custom naming — By default, Kotlin uses the file name and appends Kt

com.example.compose.HomeScreenKt\$HomeScreen
@file:JvmName("HelloScreenComposableDestination")

package com.example.compose

import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.tooling.preview.Preview
import com.example.compose.theme.YourTheme


@Composable
fun HomeScreen() {
Text("Hello")
}

@Preview
@Composable
private fun PreviewHomeComposable() {
YourTheme {
HomeScreen()
}
}
  • Nav graph with composableas destination
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/nav_graph"
app:startDestination="@+id/nav_fragmentWithContentExt">

<fragment
android:id="@+id/nav_fragmentWithContentExt"
android:name="com.example.compose.FragmentWithContentExt"
android:label="FragmentWithContentExt" />

<composable
android:id="@+id/nav_homeComposable"
android:name="com.example.compose.HomeScreenKt\$HomeScreen"
/>



</navigation>
  • Navigating to the destination using its ID
findNavController().navigate(R.id.nav_homeComposable)

Sign up to discover human stories that deepen your understanding of the world.

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

Published in ProAndroidDev

The latest posts from Android Professionals and Google Developer Experts.

Written by Nav Singh

Google Developer Expert for Android | Mobile Software Engineer at Manulife | Organizer at GDG Montreal

No responses yet

Write a response