Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use Compose inside Fragment?

The documentation describes how to create UI Jetpack Compose inside Activity.

class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContent {
        Text("Hello world!")
    }
  }
}

But how can I use it inside fragment?

like image 238
Nurseyit Tursunkulov Avatar asked Dec 17 '19 06:12

Nurseyit Tursunkulov


People also ask

Should we use fragments in compose?

First, with Compose we do not need to use Fragments. If we want to migrate an existing application it may be appropriate to have fragments wrapping composables until we complete the migration process; but for new projects, we can forget about fragments.

Does jetpack compose use fragments?

We know Jetpack Compose is the future. But many of our Apps are still having Fragments. We cannot migrate everything over all at once.

Can I use fragment inside fragment?

You can add your fragment to the activity's view hierarchy either by defining the fragment in your activity's layout file or by defining a fragment container in your activity's layout file and then programmatically adding the fragment from within your activity.

Can I use compose with XML?

If you want to incorporate Compose UI content in a fragment or an existing View layout, use ComposeView and call its setContent() method. ComposeView is an Android View . You can put the ComposeView in your XML layout just like any other View : <?


Video Answer


5 Answers

setContent on ViewGroup is now deprecated.

The below is accurate as of Compose v1.0.0-alpha01.

For pure compose UI Fragment:

class ComposeUIFragment : Fragment() {

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        return ComposeView(requireContext()).apply {
            setContent {
                Text(text = "Hello world.")
            }
        }
    }
}

For hybrid compose UI Fragment - add ComposeView to xml layout, then:

class ComposeUIFragment : Fragment() {

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        return inflater.inflate(R.layout.fragment_compose_ui, container, false).apply {
            findViewById<ComposeView>(R.id.composeView).setContent {
                Text(text = "Hello world.")
            }
        }
    }
}
like image 116
veritas1 Avatar answered Oct 16 '22 09:10

veritas1


You don't need Fragments with Compose. You can navigate to another screen without needing a Fragment or an Activity:

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            val navController = rememberNavController()
            NavHost(navController, startDestination = "welcome") {
                composable("welcome") { WelcomeScreen(navController) }
                composable("secondScreen") { SecondScreen() }
            }
        }
    }
}

@Composable
fun WelcomeScreen(navController: NavController) {
    Column {
        Text(text = "Welcome!")
        Button(onClick = { navController.navigate("secondScreen") }) {
            Text(text = "Continue")
        }
    }
}

@Composable
fun SecondScreen() {
    Text(text = "Second screen!")
}

like image 19
Cristan Avatar answered Oct 16 '22 09:10

Cristan


With 1.0.x you can :

- Define a ComposeView in the xml-layout.

  • add a androidx.compose.ui.platform.ComposeView in your layout-xml files:
    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout
        android:orientation="vertical"
         ...>
    
        <TextView ../>
    
        <androidx.compose.ui.platform.ComposeView
            android:id="@+id/compose_view"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
    
    </LinearLayout>
  • Then get the ComposeView using the XML ID, set a Composition strategy and call setContent():
    class ExampleFragment : Fragment() {
    
        override fun onCreateView(
            inflater: LayoutInflater,
            container: ViewGroup?,
            savedInstanceState: Bundle?
        ): View {
            _binding = FragmentExampleBinding.inflate(inflater, container, false)
            val view = binding.root
            view.composeView.apply {
                // Dispose the Composition when viewLifecycleOwner is destroyed
                setViewCompositionStrategy(
                    DisposeOnLifecycleDestroyed(viewLifecycleOwner)
                )
                setContent {
                    // In Compose world
                    MaterialTheme {
                        Text("Hello Compose!")
                    }
                }
            }
            return view
        }
    
        /** ... */
    }

- Include a ComposeView directly in a fragment.

    class ExampleFragment : Fragment() {
    
        override fun onCreateView(
            inflater: LayoutInflater,
            container: ViewGroup?,
            savedInstanceState: Bundle?
        ): View {
            return ComposeView(requireContext()).apply {
                // Dispose the Composition when viewLifecycleOwner is destroyed
                setViewCompositionStrategy(
                    DisposeOnLifecycleDestroyed(viewLifecycleOwner)
                )
    
                setContent {
                    MaterialTheme {
                        // In Compose world
                        Text("Hello Compose!")
                    }
                }
            }
        }
    }
like image 15
Gabriele Mariotti Avatar answered Oct 16 '22 11:10

Gabriele Mariotti


Found it:

class LoginFragment : Fragment() {

override fun onCreateView(
    inflater: LayoutInflater, container: ViewGroup?,
    savedInstanceState: Bundle?
): View? {
    // Inflate the layout for this fragment
    val fragmentView = inflater.inflate(R.layout.fragment_login, container, false)

    (fragmentView as ViewGroup).setContent {
        Hello("Jetpack Compose")
    }
    return fragmentView
}

@Composable
fun Hello(name: String) = MaterialTheme {
    FlexColumn {
        inflexible {
            // Item height will be equal content height
            TopAppBar( // App Bar with title
                title = { Text("Jetpack Compose Sample") }
            )
        }
        expanded(1F) {
            // occupy whole empty space in the Column
            Center {
                // Center content
                Text("Hello $name!") // Text label
            }
        }
    }
 }
}
like image 14
Nurseyit Tursunkulov Avatar answered Oct 16 '22 10:10

Nurseyit Tursunkulov


On my mind if you want to use Jetpack Compose with fragments in a pretty way like this

override fun onCreateView(
    inflater: LayoutInflater,
    container: ViewGroup?,
    savedInstanceState: Bundle?
) = contentView {
    Text("Hello world")
}

you can create you own extension functions for Fragments

fun Fragment.requireContentView(
    compositionStrategy: ViewCompositionStrategy = DisposeOnDetachedFromWindow,
    context: Context = requireContext(),
    content: @Composable () -> Unit
): ComposeView {
    val view = ComposeView(context)
    view.setViewCompositionStrategy(compositionStrategy)
    view.setContent(content)
    return view
}

fun Fragment.contentView(
    compositionStrategy: ViewCompositionStrategy = DisposeOnDetachedFromWindow,
    context: Context? = getContext(),
    content: @Composable () -> Unit
): ComposeView? {
    context ?: return null
    val view = ComposeView(context)
    view.setViewCompositionStrategy(compositionStrategy)
    view.setContent(content)
    return view
}

I like this approach because it looks similar to Activity's setContent { } extension

Also you can define another CompositionStrategy

override fun onCreateView(
    inflater: LayoutInflater,
    container: ViewGroup?,
    savedInstanceState: Bundle?
) = contentView(DisposeOnLifecycleDestroyed(viewLifecycleOwner)) {
    Text("Hello world")
}
like image 4
Yevhen Railian Avatar answered Oct 16 '22 10:10

Yevhen Railian