# XML To Compose Migrator

> Accurately migrate, audit, and convert Android XML layouts (activities, fragments, dialogs, bottom sheets, item views, custom views) into idiomatic Jetpack Compose code with plan-first workflows, documented roadmaps, and optional subagent execution.

- Skill: `w3wide/xml-to-compose-migrator` (Agent Skill)
- Install (CLI): `npx skillmds@latest add w3wide/xml-to-compose-migrator`
- Raw SKILL.md: https://api.skillmd.com/api/skills/w3wide/xml-to-compose-migrator/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Security
- Author: w3wide (https://skillmd.com/u/w3wide)
- Updated: 2026-09-22
- Page: https://skillmd.com/skills/w3wide/xml-to-compose-migrator

---


# XML to Jetpack Compose migrator and verifier

Use this skill when converting Android XML layouts into Jetpack Compose, or when auditing and aligning Compose screens against legacy XML layouts.

It covers full screens (Activities and Fragments), modal layouts (Dialogs and Bottom Sheets), repeated items (RecyclerView list items), and modular partials (includes and custom compound views).

## 1. Plan-first workflow for batch or project-wide migrations

When asked to migrate all XML layouts, a large group of screens, or an entire module, prepare a plan first before modifying or deleting any existing code.

### Step 1: Inventory and dependency audit

Scan all layout directories across the app and modules:

- `res/layout`
- `res/layout-land`
- Module layout folders (such as shared UI modules)

Group each XML file by component type:

1. Activities (`activity_*.xml`)
2. Fragments (`fragment_*.xml`)
3. Dialogs and Bottom Sheets (`dialog_*.xml`, `bottom_sheet_*.xml`)
4. List/Grid Items (`item_*.xml`, `list_item_*.xml`)
5. Partials and Compounds (`view_*.xml`, `layout_*.xml`, `header_*.xml`)

### Step 2: Document the migration plan in `<workspace>/docs/`

Save the plan as a Markdown file in the project's documentation folder:
`<workspace>/docs/xml_to_compose_migration_plan.md`

The document must detail:

- Current inventory table (layout file, host class, migration status, target composable path).
- Wave order (order of execution: items and dialogs first, then fragments, then host activities).
- Shared state and ViewModel bindings needed per screen.
- Theme and typography tokens mapped from XML attributes.
- Risk factors (such as third-party View dependencies, custom drawables, or legacy animations).

### Step 3: User approval

Present the plan document path to the user and request confirmation before touching code. Once approved, proceed wave by wave.

### Step 4: Subagent delegation (optional for parallel tasks)

For large migrations with independent screens (for example, five separate activity layouts that share no state), launch subagents using `invoke_subagent`:

- Assign one subagent per independent screen or feature module.
- Provide each subagent with the exact target file path, XML reference path, and verification instructions.
- Aggregate build results once all subagents finish.

---

## 2. Single layout migration workflow

When migrating or verifying a single layout file or screen, follow this four-step sequence:

1. Analyze the XML layout, associated themes, attributes, and host component (Activity, Fragment, Adapter, or Dialog).
2. Find or create the target Composable file following project conventions.
3. Translate view elements, styling, layout constraints, and interactions line by line.
4. Run Kotlin build verification and fix any compile or lint errors.

---

## 3. Deep XML analysis checklist

Before writing any Compose code, inspect these core areas:

### View to Compose element mappings

| XML view | Jetpack Compose equivalent | Notes |
|---|---|---|
| `MaterialToolbar` / `Toolbar` | `TopAppBar`, `MediumTopAppBar`, or `CenterAlignedTopAppBar` | Place inside Scaffold `topBar` slot |
| `AppBarLayout` + `CollapsingToolbarLayout` | `LargeTopAppBar` or `MediumTopAppBar` with `TopAppBarDefaults.exitUntilCollapsedScrollBehavior()` | Connect to `Modifier.nestedScroll(scrollBehavior.nestedScrollConnection)` |
| `ScrollView` / `NestedScrollView` | `Column(modifier = Modifier.verticalScroll(rememberScrollState()))` | Remember to apply safe padding |
| `HorizontalScrollView` | `Row(modifier = Modifier.horizontalScroll(rememberScrollState()))` | |
| `RecyclerView` (vertical) | `LazyColumn` | Pass list padding to `contentPadding` |
| `RecyclerView` (horizontal) | `LazyRow` | Pass list padding to `contentPadding` |
| `RecyclerView` (grid) | `LazyVerticalGrid(columns = GridCells.Fixed(n))` | |
| `ConstraintLayout` | Nested `Column` and `Row` layouts, or `ConstraintLayout` (from Compose library) | Prefer standard Rows/Columns for readability unless complex layout chains exist |
| `FrameLayout` | `Box` | Use `Alignment` modifiers for child positions |
| `LinearLayout` (vertical) | `Column` | |
| `LinearLayout` (horizontal) | `Row` | |
| `MaterialCardView` / `CardView` | `Card`, `OutlinedCard`, or `ElevatedCard` | Match XML `cardElevation` and `strokeWidth` |
| `MaterialButton` (Filled) | `Button` | |
| `MaterialButton` (Tonal) | `FilledTonalButton` | Often used for secondary/neutral actions |
| `MaterialButton` (Outlined) | `OutlinedButton` | |
| `MaterialButton` (Text) | `TextButton` | Standard for dialog actions |
| `FloatingActionButton` | `FloatingActionButton` | Place inside Scaffold `floatingActionButton` slot |
| `ExtendedFloatingActionButton` | `ExtendedFloatingActionButton` | |
| `TextView` | `Text` | Wrap in `SelectionContainer` if selectable |
| `EditText` / `TextInputEditText` | `OutlinedTextField` or `TextField` | Wire `value` and `onValueChange` state |
| `ImageView` | `Image` or `Icon` | Use `Icon` for tinted vector drawables |
| `ProgressBar` (circular) | `CircularProgressIndicator` | |
| `ProgressBar` (horizontal) | `LinearProgressIndicator` | |
| `Switch` / `MaterialSwitch` | `Switch` | |
| `CheckBox` | `Checkbox` | |
| `RadioButton` / `RadioGroup` | `RadioButton` within a selectable `Row` or `Column` | |
| `TabLayout` | `PrimaryTabRow` or `SecondaryTabRow` with `Tab` items | |
| `ViewPager2` | `HorizontalPager(state = rememberPagerState(...))` | |
| `SwipeRefreshLayout` | `PullToRefreshBox` | |
| `include` tag | Separate reusable Composable function | Keeps files modular |

### Styling and theme tokens

Never hardcode hex values when color tokens exist in your theme:

- `?attr/colorPrimary` translates to `MaterialTheme.colorScheme.primary`
- `?attr/colorSurface` translates to `MaterialTheme.colorScheme.surface`
- `?attr/colorSurfaceContainerHigh` translates to `MaterialTheme.colorScheme.surfaceContainerHigh`
- `?attr/colorOnErrorContainer` translates to `MaterialTheme.colorScheme.onErrorContainer`
- Custom project theme extensions translate to corresponding `MaterialTheme` extension properties
- Ripple effects and highlights (`?attr/colorControlHighlight`) use default Material 3 interactions

### Typography and strings

- Map `android:textAppearance="?attr/textAppearanceTitleLarge"` to `MaterialTheme.typography.titleLarge`.
- Map `android:textStyle="bold"` to `fontWeight = FontWeight.Bold`.
- Map `android:fontFamily` to the project's configured font family.
- Always load strings using `stringResource(R.string.your_string_name)` instead of hardcoding English text.

### Interactive states and accessibility

- `android:textIsSelectable="true"`: Wrap the `Text` composable inside `SelectionContainer { Text(...) }`. Crucial for stack traces, error reports, and IDs.
- `android:contentDescription`: Always supply a descriptive string or `null` for decorative elements.
- `android:enabled="false"`: Pass `enabled = false` to the composable.
- `android:maxLines="N"`: Pass `maxLines = N, overflow = TextOverflow.Ellipsis`.

---

## 4. Handling different layout types

### Type A: Full screen Activity layouts (`activity_*.xml`)

1. Add `enableEdgeToEdge()` in the Activity's `onCreate()` before `setContent`.
2. Wrap content in `Scaffold`.
3. Pass `innerPadding` from `Scaffold` to scroll containers using `contentPadding = innerPadding` or `.padding(innerPadding).consumeWindowInsets(innerPadding)` to prevent content from hiding beneath navigation and status bars.

### Type B: Fragment layouts (`fragment_*.xml`)

1. Create the screen composable `Screen.kt`.
2. In `Fragment.onCreateView`, return `ComposeView`:

```kotlin
override fun onCreateView(
    inflater: LayoutInflater,
    container: ViewGroup?,
    savedInstanceState: Bundle?
): View {
    return ComposeView(requireContext()).apply {
        setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
        setContent {
            YourFragmentScreen(...)
        }
    }
}
```

### Type C: Bottom sheets and dialogs

1. Map `BottomSheetDialogFragment` to `ModalBottomSheet(onDismissRequest = { ... })`.
2. Map `AlertDialog` to `AlertDialog` or custom `Dialog` composable.
3. Use `FilledTonalButton` or `TextButton` for dismiss and confirm actions.

### Type D: RecyclerView item layouts (`item_*.xml`, `list_item_*.xml`)

1. Convert the XML into a standalone item composable (for example `UserListItem(...)`).
2. Supply clear callback parameters for user interactions like `onClick: (Item) -> Unit` and `onLongClick: (() -> Unit)? = null`.
3. Keep the item composable stateless. Pass data through model objects.

### Type E: Compound views and included layouts (`view_*.xml`, `layout_*.xml`)

1. Convert each `<include layout="@layout/header_view" />` into an independent composable function.
2. Group related UI state into plain data classes if more than four parameters are passed.

---

## 5. Detection and target naming rules

Search existing files before generating new ones:

- `activity_profile.xml` maps to `ProfileScreen.kt` in `ui/profile/`
- `activity_manage_project.xml` maps to `ManageProjectScreen.kt` in `ui/project/`
- `item_product.xml` maps to `ProductItem.kt` or `ProductListItem.kt` in `ui/store/components/`
- `dialog_alert_warning.xml` maps to `WarningAlertDialog.kt` in `ui/components/`

When in doubt, search the codebase with git or directory tools to verify whether an existing composable already serves the screen.

---

## 6. Verification and build checks

After making code changes, verify your work:

```bash
./gradlew :app:compileDebugKotlin
```

Resolve any compile errors immediately, including:

- Deprecated composables or modifiers (such as `menuAnchor()` or `TabRow`).
- Missing extended Material icons (ensure `material-icons-extended` is imported if needed).
- Unconsumed window insets causing duplicate padding.

