Generated banner for Migrate Xml Views To Jetpack Compose
Generated logo for Migrate Xml Views To Jetpack Compose

Migrate Xml Views To Jetpack Compose

Android (Google)OfficialCreated by Google LLCDistributed by Android (Google)

  • Skill
  • Workflow
  • Software development
  • Apache-2.0
  • No issues found

From the author

Provides a structured workflow for migrating an Android XML View to Jetpack Compose. This skill details the step-by-step process, from planning and dependency setup, to theming and layout migration, validation and XML cleanup. Use this skill when you need to migrate an XML View to Jetpack Compose in an Android project. It solves the problem of converting the UI of a legacy XML View into modern, declarative Compose components while maintaining interoperability.
— the description by Google LLC

AutoGineer summary

Written by AutoGineer from the skill’s own files, not by the author

This skill provides a systematic methodology for converting Android XML Views into declarative Jetpack Compose components. It emphasizes maintaining visual parity and functional integrity through a rigorous process that includes project analysis, dependency setup, theming alignment, and validation. The workflow is designed for incremental adoption, ensuring that legacy XML and new Compose code can coexist through interoperability APIs while preparing the codebase for a full transition to Compose.

What it does

  • Analyzes existing XML layouts and project infrastructure for migration readiness.
  • Guides the setup of Compose dependencies and compiler configurations.
  • Facilitates the migration of XML themes to Material 3 Compose theming.
  • Provides a structured approach to replacing XML View usages with Compose components.
  • Ensures visual parity through automated screenshot testing and Compose Previews.
  • Manages the safe removal of legacy XML files and associated resources.

When to use it

  • When modernizing an Android application's UI layer from XML to Jetpack Compose.
  • When you need to perform an incremental migration of specific UI components.
  • When you need to ensure visual consistency between legacy XML and new Compose code.
  • When integrating new Compose features into an existing View-based Android project.

Advantages

  • Reduces risk by following a validated, step-by-step migration methodology.
  • Maintains project stability through interoperability best practices.
  • Ensures visual fidelity using baseline screenshot comparisons.
  • Promotes clean code by systematically removing redundant legacy XML resources.

Usage examples

“Help me migrate my login_screen.xml to Jetpack Compose.”
The agent will initiate the 10-step workflow, starting with an analysis of the login_screen.xml structure and project dependencies to create a migration plan.
“How do I handle theming when moving from XML to Compose?”
The agent will guide you through mapping XML color attributes and styles to Material 3 semantic roles within a Compose MaterialTheme.

Install

  1. Download the skill as a ZIP:

    https://mp-staging.autogineer.com/agent-skills/android/migrate-xml-views-to-jetpack-compose/download
  2. Unzip it into the cross-client skills folder of your project:

    mkdir -p .agents/skills && unzip migrate-xml-views-to-jetpack-compose.zip -d .agents/skills

Documentation by Google LLC

This skill guides through the process of migrating an existing Android XML View to Jetpack Compose. It performs a stable, safe and visually consistent transition by following a structured, 10-step methodology. This skill migrates UI (XML to Jetpack Compose) only.

Objective

To systematically convert a single legacy XML layout into modern, declarative Jetpack Compose UI while maintaining pixel-perfect visual parity and functional integrity.

Summary of the 10-step migration process

  1. Identify the optimal XML candidate for migration

  2. Analyze the project and layout

  3. Create a plan

  4. Capture the XML View UI

  5. Set up Compose dependencies and compiler

  6. Set up Compose theming

  7. Migrate the XML layout to Compose

  8. Validate the migration

  9. Replace usages

  10. XML code removal

Detailed steps

Step 1: Identify the optimal XML candidate for migration

If the user has explicitly specified a target XML layout, proceed to Step 2. Otherwise, analyze the codebase to identify the best candidate for migration by following the logic in references/identify-optimal-xml-candidate.md.

Step 2: Analyze the project and layout

Analyze the identified XML View's structure, hierarchy, and implementation details. Use references/analysis-of-the-project-and-layout.md to guide your technical audit of the layout and surrounding project context.

Step 3: Create a plan

Using the outputs and analysis done in the Step 1 and 2, generate a step-by-step plan for the migration. If you support user interaction, present to the user and ask for approval before proceeding. If user interaction is not supported, proceed to Step 4 following the generated plan.

Step 4: Capture the XML View UI

IF you support user interaction, ask the user to upload a screenshot of the XML View UI or provide an absolute path to a file. Use this image as a visual reference for the layout migration in Step 7. ELSE IF you are able to run an Android emulator, locate an existing screenshot test for the XML candidate. If none exists, create one using the existing project testing framework. If no framework exists, use UI Automator or Espresso to create a screenshot test with minimum required setup. Run the test and take a baseline screenshot of the XML UI. ELSE proceed to Step 5.

Step 5: Set up Compose dependencies and compiler

Check build.gradle or libs.versions.toml for Compose dependencies and compiler setup. If missing, use Setup Compose Dependencies and Compiler. Run a sync to ensure dependencies resolve without errors.

Step 6: Set up Compose theming

If the project already has Compose theming set up, proceed to Step 7. If Compose theming is missing, initialize it. For Material-based projects, follow Material 3 migration guidelines. For custom design systems, apply expert judgment to migrate XML theming and match existing styles. Constraints: Do not migrate the entire theme. Implement only the minimum theming required for the specific XML candidate. Maintain original XML themes for interoperability. Maintain existing project code conventions, patterns, names and values.

Step 7: Migrate the XML View to Compose

Convert the XML candidate to Jetpack Compose code, referencing references/xml-layout-migration.md and the image from Step 4. You must include a Compose Preview for the newly created composable to facilitate visual verification.

Step 8: Replace usages

Replace the usages of the migrated XML layout to use the new Compose component.

Step 9: Validate the migration

Compare the baseline screenshot image from Step 4 with the rendered Compose Preview of the new composable. Ignore string content; focus on layout and styling. Iterate on the Compose code until visual parity is achieved. Once verified, write a Compose UI test for the new composable.

Step 10: XML code removal

Delete the migrated XML file and its associated legacy tests. Caution: Only remove code and resources that are not referenced by other parts of the project.

Files

references/android/develop/ui/compose/migrate/interoperability-apis/compose-in-views.md

[!NOTE] Note: Interoperability is supported for hybrid apps that use Compose and Views. For apps that are only in Compose, use the recommended Compose-only architecture with a single Activity and latest navigation libraries, like Navigation 3.

You can add Compose-based UI into an existing app that uses a View-based design.

To create a new, entirely Compose-based screen, have your activity call the setContent() method, and pass whatever composable functions you like.

class ExampleActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        setContent { // In here, we can call composables!
            MaterialTheme {
                Greeting(name = "compose")
            }
        }
    }
}

@Composable
fun Greeting(name: String) {
    Text(text = "Hello $name!")
}

<br />

This code looks just like what you'd find in a Compose-only app.

[!CAUTION] Caution: To use the ComponentActivity.setContent method, add the androidx.activity:activity-compose:$latestVersion dependency to your build.gradle file.

See the Activity releases page to find out the latest version.

ViewCompositionStrategy for ComposeView

ViewCompositionStrategy defines when the Composition should be disposed. The default, ViewCompositionStrategy.Default, disposes the Composition when the underlying ComposeView detaches from the window, unless it is part of a pooling container such as a RecyclerView. In a single-Activity Compose-only app, this default behavior is what you would want, however, if you are incrementally adding Compose in your codebase, this behavior may cause state loss in some scenarios.

To change the ViewCompositionStrategy, call the setViewCompositionStrategy() method and provide a different strategy.

The table below summarizes the different scenarios you can use ViewCompositionStrategy in:

ViewCompositionStrategyDescription and Interop Scenario
DisposeOnDetachedFromWindowThe Composition will be disposed when the underlying ComposeView is detached from the window. Has since been superseded by DisposeOnDetachedFromWindowOrReleasedFromPool. Interop scenario: * ComposeView whether it's the sole element in the View hierarchy, or in the context of a mixed View/Compose screen (not in Fragment).
DisposeOnDetachedFromWindowOrReleasedFromPool (Default)Similar to DisposeOnDetachedFromWindow, when the Composition is not in a pooling container, such as a RecyclerView. If it is in a pooling container, it will dispose when either the pooling container itself detaches from the window, or when the item is being discarded (i.e. when the pool is full). Interop scenario: * ComposeView whether it's the sole element in the View hierarchy, or in the context of a mixed View/Compose screen (not in Fragment). * ComposeView as an item in a pooling container such as RecyclerView.
DisposeOnLifecycleDestroyedThe Composition will be disposed when the provided Lifecycle is destroyed. Interop scenario * ComposeView in a Fragment's View.
DisposeOnViewTreeLifecycleDestroyedThe Composition will be disposed when the Lifecycle owned by the LifecycleOwner returned by ViewTreeLifecycleOwner.get of the next window the View is attached to is destroyed. Interop scenario: * ComposeView in a Fragment's View. * ComposeView in a View wherein the Lifecycle is not known yet.

ComposeView in Fragments (Transitionary Step)

[!NOTE] Note: For hybrid apps with Views and Compose, that use Fragments, use ComposeView to wrap composables content and add to a Fragment during migration. For Compose-only apps, do not use Fragments and instead use the recommended Compose-only architecture with a single Activity and latest navigation libraries, like Navigation 3.

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:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

  <TextView
      android:id="@+id/text"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content" />

  <androidx.compose.ui.platform.ComposeView
      android:id="@+id/compose_view"
      android:layout_width="match_parent"
      android:layout_height="match_parent" />
</LinearLayout>

In the Kotlin source code, inflate the layout from the layout resource defined in XML. Then get the ComposeView using the XML ID, set a Composition strategy that works best for the host View, and call setContent() to use Compose.

class ExampleFragmentXml : 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 {
            // Dispose of the Composition when the view's LifecycleOwner
            // is destroyed
            setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
            setContent {
                // In Compose world
                MaterialTheme {
                    Text("Hello Compose!")
                }
            }
        }
        return view
    }
}

<br />

Alternatively, you can also use view binding to obtain references to the ComposeView by referencing the generated binding class for your XML layout file:

class ExampleFragment : Fragment() {

    private var _binding: FragmentExampleBinding? = null

    // This property is only valid between onCreateView and onDestroyView.
    private val binding get() = _binding!!

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        _binding = FragmentExampleBinding.inflate(inflater, container, false)
        val view = binding.root
        binding.composeView.apply {
            // Dispose of the Composition when the view's LifecycleOwner
            // is destroyed
            setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
            setContent {
                // In Compose world
                MaterialTheme {
                    Text("Hello Compose!")
                }
            }
        }
        return view
    }

    override fun onDestroyView() {
        super.onDestroyView()
        _binding = null
    }
}

<br />

Image: Two slightly different text elements, one above the other

Figure 1. This shows the output of the code that adds Compose elements in a View UI hierarchy. The "Hello Android!" text is displayed by a TextView widget. The "Hello Compose!" text is displayed by a Compose text element.

You can also include a ComposeView directly in a fragment if your full screen is built with Compose, which lets you avoid using an XML layout file entirely.

class ExampleFragmentNoXml : Fragment() {

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        return ComposeView(requireContext()).apply {
            // Dispose of the Composition when the view's LifecycleOwner
            // is destroyed
            setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
            setContent {
                MaterialTheme {
                    // In Compose world
                    Text("Hello Compose!")
                }
            }
        }
    }
}

<br />

Multiple ComposeView instances in the same layout

If there are multiple ComposeView elements in the same layout, each one must have a unique ID for savedInstanceState to work.

class ExampleFragmentMultipleComposeView : Fragment() {

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View = LinearLayout(requireContext()).apply {
        addView(
            ComposeView(requireContext()).apply {
                setViewCompositionStrategy(
                    ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed
                )
                id = R.id.compose_view_x
                // ...
            }
        )
        addView(TextView(requireContext()))
        addView(
            ComposeView(requireContext()).apply {
                setViewCompositionStrategy(
                    ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed
                )
                id = R.id.compose_view_y
                // ...
            }
        )
    }
}

<br />

The ComposeView IDs are defined in the res/values/ids.xml file:

<resources>
  <item name="compose_view_x" type="id" />
  <item name="compose_view_y" type="id" />
</resources>

Preview composables in Layout Editor

You can also preview composables within the Layout Editor for your XML layout containing a ComposeView. Doing so lets you see how your composables look within a mixed Views and Compose layout.

Say you want to display the following composable in the Layout Editor. Note that composables annotated with @Preview are good candidates to preview in the Layout Editor.

@Preview
@Composable
fun GreetingPreview() {
    Greeting(name = "Android")
}

<br />

To display this composable, use the tools:composableName tools attribute and set its value to the fully qualified name of the composable to preview in the layout.

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

  <androidx.compose.ui.platform.ComposeView
      android:id="@+id/my_compose_view"
      tools:composableName="com.example.compose.snippets.interop.InteroperabilityAPIsSnippetsKt.GreetingPreview"
      android:layout_height="match_parent"
      android:layout_width="match_parent"/>

</LinearLayout>

Image: Composable displayed within layout editor

Requirements and compatibility

Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta

“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.

Security

No issues found by our static scan· No issues found

A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.

Attribution and licence

Created by
Google LLC
Publisher
Android (Google) (Official)
Distributed by
Android (Google)
Source
android/skills · jetpack-compose/migration/migrate-xml-views-to-jetpack-compose · commit bf68e2d
Licence
Apache License 2.0 (redistribution allowed)
  • LICENSE (added by AutoGineer from LICENSE.txt)
  • ATTRIBUTION.md (written by AutoGineer)
Synced

AutoGineer is not affiliated with or endorsed by Google LLC or Android (Google).

Download ZIP

The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download migrate-xml-views-to-jetpack-compose.zip