How to use Compose inside Fragment?
Solution 1
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
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
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
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
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")
}
Related videos on Youtube
Nurseyit Tursunkulov
Updated on January 25, 2022Comments
-
Nurseyit Tursunkulov over 2 years
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?
-
Kaushik Burkule over 4 yearsPlease edit your question to provide a minimal reproducible example. Up to now your question is kind of vague, making it hard to see what you are doing and where the problem is.
-
Nouman Shah over 4 yearsif am not sure if i got you , use framelayout inside your activity the fragment have there own life cycle and xml file you can use that here is offical documentation link developer.android.com/guide/components/fragments
-
Admin over 4 yearsok i give you a upvote now enjoy the code.. i have posted the code..
-
-
oiyio over 3 yearscan you share the content of fragment_login.xml ?
-
Przemo over 3 yearsit's deprecated (tested on alpha12)
-
Gabriel Vasconcelos over 3 yearsWhile that is true, it does not invalidate the use case of having a fragment serving as the backbone, probably a common scenario for migrating codebases. So that does not answer the question.
-
Jason Crosby almost 3 yearsWhile this is possible it won't be feasible in apps with more than a couple screens.
-
Cristan almost 3 years@JasonCrosby I don't see why you wouldn't use this in apps with a lot of screens. Yes, the
NavHost
will grow, but you can easily extract this to a separate file so your main activity will stay lean. -
Jason Crosby almost 3 yearsPutting all of your compose code in one activity would end up with an activity thousands of lines long. Maybe even more.
-
Cristan almost 3 yearsTrue. That's why in the real world, you shouldn't actually code like in my example. Each screen (like
WelcomeScreen
andSecondScreen
) should be in its own file instead of in MainActivity.kt. Same applies for the navigation. -
BoD almost 3 yearsWhat about app logic (which you would usually put in ViewModels)?
-
Cristan almost 3 yearsApp logic should remain in ViewModels: developer.android.com/jetpack/compose/state#viewmodel-state
-
Melvynx over 2 yearsThanks, the
setViewCompositionStrategy
resolve my problem -
klenki over 2 years
setViewCompositionStrategy(DisposeOnViewTreeLifecycleDestroyed)
in addition the composition strategy should be reviewed when doing this. developer.android.com/jetpack/compose/interop/… -
Sam over 2 yearsHow to get the NavController in Fragment ?
-
EpicPandaForce almost 2 yearsThis might apply if you're using
navigation-compose
, but the question was about Fragments. As Fragments, unlike Navigation-Compose, support screen transitions and type-safe argument passing, etc. -
EpicPandaForce almost 2 yearsYou can also use
setViewCompositionStrategy(DisposeOnViewTreeLifecycleDestroyed)