From XML to Jetpack Compose: Migrating a MVVM Android Project

Jyoti Sheoran
Getir
Published in
3 min readApr 28, 2023
Photo by Adi Goldstein on Unsplash

Our project was an MVVM Android application with multiple modules containing a fragment, a ViewModel, a domain layer, and a repository layer. We had been using XML-based layouts and wanted to migrate to Jetpack Compose to take advantage of its modern, declarative UI toolkit. Our project’s decoupled architecture made it an ideal candidate for gradual migration.

Part 1: Changing the UI Layer To begin

We started migrating our old fragments to Jetpack Compose methods, writing new fragments’ UI in Compose. This step was done gradually, one screen at a time. As Jetpack Compose is fully interoperable with XML-based views, we could mix and match Compose methods and XML views.

Here’s an overview of the basic setup needed for adding Jetpack Compose to an Android project:

  1. First, add the Compose dependency to your project’s build.gradle file:
dependencies {
// ...

def compose_version = "1.1.0-alpha04"
implementation "androidx.compose.ui:ui:$compose_version"
implementation "androidx.compose.material:material:$compose_version"
implementation "androidx.compose.ui:ui-tooling:$compose_version"
}

2. Next, you need to set the buildFeatures in your build.gradle file to enable Jetpack Compose:

android {
// ...

buildFeatures {
compose true
}

// ...

}

3. You can create a new module and add a new colors.xml file to it. You can define your custom colors here, which can be accessed from any module in your project.

4. To use dimensions in Jetpack Compose, you can access them via the dimenResource function. For example, to set the padding of a Text composable:

Text(
text = "Hello, World!",
modifier = Modifier.padding(dimenResource(id = R.dimen.my_padding))
)

5. To start with Jetpack Compose, you can pick an easy screen from your app, for example, the profile screen. You can then create a new Compose method for that screen, such as ProfileScreen(), and start refactoring your existing code to use Compose instead of XML-based views.

Here’s an example of how you could refactor the ProfileFragment using Jetpack Compose:

@Composable
fun ProfileScreen() {
Surface(color = MaterialTheme.colors.background) {
Column(modifier = Modifier.padding(16.dp)) {
Text(text = "John Doe", style = MaterialTheme.typography.h4)
Text(text = "johndoe@email.com", style = MaterialTheme.typography.body1)
Button(
onClick = { /* Open edit profile screen */ },
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 16.dp)
) {
Text(text = "Edit Profile")
}
}
}
}

class ProfileFragment : Fragment() {

override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
return ComposeView(requireContext()).apply {
setContent {
ProfileScreen()
}
}
}
}

In this example, we create a ProfileScreen composable function that uses the Column, Text, and Button composable functions to create the UI for the profile screen. The ComposeView in the ProfileFragment is used to inflate and display the Compose UI.

For other examples refer to Converting XML UI to Jetpack Compose

Part 2: Migrate Navigation

This step involved refactoring our activity and removing fragments, to fully work with Jetpack Compose. We did these steps at once, but it could also be done gradually,

  1. You can remove dependencies on fragment lifecycle methods and add those in Compose, such as permission requests and dialogues.
  2. Then, you can use Jetpack Compose Navigation for navigation and remove fragments entirely.

Part 3: Migrating to MVI Architecture [Optional]

We decided to take advantage of Jetpack Compose’s state management by migrating to the MVI (Model-View-Intent) architecture. We migrated our ViewModels to MVI ViewModels, which helped us simplify the logic of our UI layer.

Here are some articles that will assist you in getting started with MVI (Model-View-Intent) architecture:

  1. Introduction to MVI Architecture Pattern in Android
  2. Implementing MVI with Reducers in Android: A Step-by-Step Tutorial

Conclusion:

Migrating to Jetpack Compose was a challenging but rewarding experience. We found that the modern UI toolkit provided more flexibility and a better developer experience. We recommend breaking down the migration into smaller steps, starting with the UI layer and then moving on to the architecture and base activity/fragments. Finally, we suggest considering MVI architecture to take full advantage of Jetpack Compose’s state management capabilities.

--

--