Recommended Reading

11 Minutes
Jetpack Compose Fragments: How to Migrate and Integrate in Android Apps
Fix Bugs Faster! Log Collection Made Easy
Imagine you’re a toolmaker. You’ve got an old-school toolshed with loads of ancient equipment: hammers, pickaxes, all the stuff people used hundreds of years ago. And then one day you walk into your toolshed to find laser cutters, 3D printers and loads of other digital tools at your disposal. Life would suddenly seem a lot simpler, right?
Table of Contents
Well, that’s kind of how it’s been for developers moving from old-school XML coding to Jetpack Compose, the modern toolkit made by Google to build UI for Android apps. There are loads of benefits to Jetpack Compose that we could mention, but here are the most common ones:
- We don’t need to write code in an XML file separately; we simply need to write code in the Kotlin class.
- This means we can create a solid product that’s fully compatible and adoptable with existing code content, so we don’t need to take care of it via a separate route.
- What’s more, the data will automatically refresh when its state has been changed, which saves a lot of time.
- And Jetpack Compose provides a ready-to-use collection of layouts to get you started.
Best of all, Jetpack Compose allows us to denote UI behaviors with the @Composable
annotation, which means we can easily implement each new feature with minimal code content. For example, here’s a button we’ve built using Jetpack Compose. Look at how nice and simple the code is in this window:
@Composable
fun ButtonExample(onClick: () -> Unit) {
Button(onClick = { Log.d("Button : ", "button clicked.") }) {
Text("Click Button")
}
}
But there’s one really important thing to know with Jetpack Compose. It doesn’t use fragments natively.
Composable
(and Navigation
) are designed to replace Fragments
. For new apps, with no legacy code, this is fine. But if we’re migrating old projects, which contain fragments, this may seem like a hassle.
So, today, we’re going to show you how to handle fragments in Jetpack Compose, and all the stuff that comes with them, with plenty of code samples.
The article is going to be useful for:
- Entry-level Android developers who want some simple explainer content around Jetpack UI.
- Advanced developers who have a specific fragment they want to port over to Jetpack Compose and don’t have a method for it.
- Developers who are just looking for advanced coding topics to sharpen their game and add new tools to their kit.
Ready? Let’s dive in. (Remember: if you have questions about anything you read here, we’re happy to provide support).
Part 1: The basics of Jetpack Compose fragments
How do we create a Jetpack Compose fragment?
Jetpack Compose is fully declarative. This means that we can simply call the function of the Jetpack Compose UI, and this will transform our data to a UI hierarchy automatically.
However, this also means that if we are migrating fragments from an old project, we are obliged to declare ComposeView
inside the fragment itself.
Now, how do we do this? Well, there’s a workaround here. You can embed Jetpack Compose inside a fragment using ComposeView
, as shown in the example below.
class ExampleFragmentWithoutXml : Fragment() {
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
return ComposeView(requireContext()).apply {
setContent {
Text("Hello Its a basic text Compose!")
}
}
}
}
Now let’s take a closer look without Jetpack Compose
In this case, we need to put the ComposeView
in our XML layout file. Here’s what the code looks like.
<LinearLayout xmlns:android="<http://schemas.android.com/apk/res/android>"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.compose.ui.platform.ComposeView
android:id="@+id/compose_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
Once we’ve done this, we have to inflate the XML layout file in our Kotlin code.
class ExampleFragmentUsingXml : Fragment() {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
val view = inflater.inflate(R.layout.fragment_example, container, false)
val composeView = view.findViewById<ComposeView>(R.id.compose_view)
composeView.apply {
setContent {
Text("Hello Anupam")
}
}
return view
}
}
And we’re done! Nice and straightforward so far, right?
Part 2: A more advanced view
Now, how do we create a composable?
As mentioned, a Composable in Jetpack Compose is a function annotated with @Composable
that defines a piece of UI. When we mark a function with the @Composable
annotation, this means the function will be used to build UI elements.
So let’s create a basic Composable function that displays text:
@Composable
fun MyComposeText(name: String) {
Text(text = "Hi, $name!")
}
// To update UI
@Composable
fun showCmposeText() {
MyComposeText("I am Anupam") // call compose function
}
Here, Text
is the inbuilt compose function that will be used to display text values. We previously used this in our XML file with the <TextView>
tag.
To update the UI dynamically, we need to use the composable functions like remember
and mutableStateOf
, like the code below.
@Composable
fun ShowButtonClickCountInText() {
var i by remember { mutableStateOf(0) } // State variable
Button(onClick = { i++ }) {
Text("Count is : $i")
}
}
Ok, so how do we deal with dependencies?
A dependency tag is used to implement libraries, and it’s declared in the build.gradle.kts
file.
For any Android projects, dependencies are managed through Gradle
. As we know, compose is part of the Jetpack library, so we need to implement the required library in the Gradle file.
Before adding the library, we need to ensure that the correct build file (Module: App) is open. Within this file, we need to place the link under the dependency
tag.
In addition, to enable compose functionality, we need to set compose=true
under the buildFeatures
tag.
android {
buildFeatures {
compose = true // Enable Jetpack Compose
}
}
If you found any type of conflict, such as “version” or “can’t resolve”, then use strictly
to enforce a specific version, like this.
dependencies {
implementation("androidx.compose.ui:ui") {
version { strictly("1.6.0") }
}
}
If we want to update the compose dependency with the latest version, then we need to update the project-level build.gradle.kts
file.
plugins {
id("com.github.ben-manes.versions") version "0.50.0"
}
Now, let’s add a few dependencies
We can easily add a few dependencies that are necessary for the Jetpack Compose project, including the core Compose UI.
Here’s the code to show you what that destination looks like.
dependencies {
// Compose BOM (Manages all versions automatically)
implementation(platform("androidx.compose:compose-bom:2024.02.02"))
// Core Compose UI
implementation("androidx.activity:activity-compose:1.9.0")
implementation("androidx.compose.ui:ui")
implementation("androidx.compose.material3:material3")
// Tooling for debugging
debugImplementation("androidx.compose.ui:ui-tooling")
// ViewModel integration
implementation("androidx.lifecycle:lifecycle-viewmodel-compose")
// Navigation (If using Compose Navigation)
implementation("androidx.navigation:navigation-compose")
}
Part 3: Trouble-shooting
Firstly, how do we deal with reflection?
When we talk about reflection, we’re talking about code that can inspect other code.
Reflection is commonly used in tasks such as serialization (where we convert the object state into a storable format), but this isn’t usually a good thing, because it can increase memory usage, slow your execution and require the usage of Dagger, a dependency injection framework that can greatly increase build time.
For example, when analyzing the Employee
class, we may be tempted to use the Gson
library. This will involve reflection, and thus impact performance.
val gson = Gson()
val user = gson.fromJson(jsonString, Employee::class.java)
However, as a Kotlin-based framework, the Jetpack Compose library usually avoids these problems. In the case of serialization, Kotlinx
serialization is compiler-based, so there is no need to use reflection.
@Serializable
data class Employee(val name: String, val empcode: Int)
val json = Json.encodeToString(Employee("Anupam", 1))
val user = Json.decodeFromString<Employee>(json)
Ok, but we know what you’re asking here: what if we absolutely need to use reflection? How do we use it without sacrificing performance?
In this case, we need to update the rules for two code-shrinking tools, ProGuard
and R8
. Both of these tools will identify and remove unused code, keeping your build nice and lean. Here’s how we put these into practice.
// com.example.MyClass replce with your own class with package name
-keep class com.example.MyClass { *; }
-keep class kotlinx.serialization.** { *; }
Right. Now, how do we deal with Jetpack Compose memory leaks?
Memory leaks are a common issue that can affect any kind of Android application. Jetpack Compose has loads of advantages (as you’ve hopefully seen by now!) but it can still suffer memory leaks due to poor lifecycle management practices, such as the retention of objects, forgetting to get rid of observers and not dealing properly with lambda expressions, which are crucial to Jetpack Compose.
Here’s an example of what happens if we use context
in a ViewModel
.
class MyViewModel(context: Context) : ViewModel() { // Can leak Activity
private val myContext = context
}
However, we can significantly reduce memory leaks in applications by deploying applicationContext
instead of context
or activity .
class MyViewModel(application: Application) : AndroidViewModel(application) {
private val context = getApplication<Application>().applicationContext //Safe to use
}
We can also use the leakcanary
library to detect memory leaks in Android applications. We need to add the dependency in the Gradle file the following way.
dependencies {
debugImplementation("com.squareup.leakcanary:leakcanary-android:2.12")
}
Ok. Any other lifecycle management tips for me?
Why sure!
Jetpack Compose does not use activity
or fragment
lifecycle methods like onCreate()
, onStart()
, and onDestroy()
. Instead, when Jetpack Compose runs your composable function the first time, it is called initial composition.
During this time, Jetpack Compose keeps track of all the composables that will be used to build the UI. Then, when the state of our app has been changed (as with a text update or a color change), it will automatically update the UI without calling it again. This is called recomposition.

Composition can be called single-time in code, but recomposition is used multiple times in code. The instance of composable is distinct by each composition.
@Composable
fun MyComposable() {
Column {
Text("Anupam")
Text("Jetpack")
Text("Compose")
}
}
In the above example, we use three pieces of text within a column. Here, when the composable enters the composition, three instances will create. Each has its own lifecycle in the composition, as you can see here.
And finally…how do we handle errors in Jetpack Compose?
We can handle errors in Jetpack compose in several ways. It depends on which type of error we are dealing with, like UI, network, or exception.
Here’s an example of an UI error handle. As you can see, we can use try catch block to handle the UI error. This is really useful.
@Composable
fun ShowUserName(userName: String) {
try {
Text("Welcome, $userName")
} catch (e: Exception) {
Text("Failed to load user details")
}
}
We may write a further blog in due course, highlighting how to handle errors in Jetpack Compose.
To recap
Jetpack Compose is the future of Android UI that replaces the XML layout with Kotlin code. If we have older code, then we can easily migrate it in Jetpack Compose. Jetpack provides an in-built function; we need to call in our code using a parameter, and the view will automatically build up.
We don’t need fragments with Compose. We can navigate to another screen without needing a fragment or an activity. Using compose navigation, we can navigate from one screen to another screen. But if we are going to use Compose, there are lots of workarounds we can use.
If you have any questions about Android Jetpack Compose Fragment, don’t be afraid to reach out to us. Happy Coding!
Expect The Unexpected!
Debug Faster With Bugfender