How to use Compose inside Fragment?
AndroidAndroid Jetpack-ComposeAndroid Problem Overview
The documentation describes how to create UI (Jetpack Compose https://developer.android.com/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?
Android Solutions
Solution 1 - Android
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.")
}
}
}
}
Solution 2 - Android
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!")
}
Solution 3 - Android
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
}
}
}
}
}
Solution 4 - Android
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 callsetContent()
:
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!")
}
}
}
}
}
Solution 5 - Android
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")
}