client/.github/skills/xml-to-compose-migration/SKILL.md
Convert Android XML layouts to Jetpack Compose. Use when asked to migrate Views to Compose, convert XML to Composables, or modernize UI from View system to Compose.
npx skillsauth add ahaodev/heji xml-to-compose-migrationInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Systematically convert Android XML layouts to idiomatic Jetpack Compose, preserving functionality while embracing Compose patterns. This skill covers layout mapping, state migration, and incremental adoption strategies.
ConstraintLayout, LinearLayout, FrameLayout, etc.).@{}) or view binding references.include, merge, or ViewStub usage.ComposeView/AndroidView).Apply the layout mapping table below to convert each View to its Compose equivalent.
LiveData observation to StateFlow collection or observeAsState().findViewById / ViewBinding with Compose state.| XML Layout | Compose Equivalent | Notes |
|------------|-------------------|-------|
| LinearLayout (vertical) | Column | Use Arrangement and Alignment |
| LinearLayout (horizontal) | Row | Use Arrangement and Alignment |
| FrameLayout | Box | Children stack on top of each other |
| ConstraintLayout | ConstraintLayout (Compose) | Use createRefs() and constrainAs |
| RelativeLayout | Box or ConstraintLayout | Prefer Box for simple overlap |
| ScrollView | Column + Modifier.verticalScroll() | Or use LazyColumn for lists |
| HorizontalScrollView | Row + Modifier.horizontalScroll() | Or use LazyRow for lists |
| RecyclerView | LazyColumn / LazyRow / LazyGrid | Most common migration |
| ViewPager2 | HorizontalPager | From accompanist or Compose Foundation |
| CoordinatorLayout | Custom + Scaffold | Use TopAppBar with scroll behavior |
| NestedScrollView | Column + Modifier.verticalScroll() | Prefer Lazy variants |
| XML Widget | Compose Equivalent | Notes |
|------------|-------------------|-------|
| TextView | Text | Use style → TextStyle |
| EditText | TextField / OutlinedTextField | Requires state hoisting |
| Button | Button | Use onClick lambda |
| ImageView | Image | Use painterResource() or Coil |
| ImageButton | IconButton | Use Icon inside |
| CheckBox | Checkbox | Requires checked + onCheckedChange |
| RadioButton | RadioButton | Use with Row for groups |
| Switch | Switch | Requires state hoisting |
| ProgressBar (circular) | CircularProgressIndicator | |
| ProgressBar (horizontal) | LinearProgressIndicator | |
| SeekBar | Slider | Requires state hoisting |
| Spinner | DropdownMenu + ExposedDropdownMenuBox | More complex pattern |
| CardView | Card | From Material 3 |
| Toolbar | TopAppBar | Use inside Scaffold |
| BottomNavigationView | NavigationBar | Material 3 |
| FloatingActionButton | FloatingActionButton | Use inside Scaffold |
| Divider | HorizontalDivider / VerticalDivider | |
| Space | Spacer | Use Modifier.size() |
| XML Attribute | Compose Modifier/Property |
|---------------|--------------------------|
| android:layout_width="match_parent" | Modifier.fillMaxWidth() |
| android:layout_height="match_parent" | Modifier.fillMaxHeight() |
| android:layout_width="wrap_content" | Modifier.wrapContentWidth() (usually implicit) |
| android:layout_weight | Modifier.weight(1f) |
| android:padding | Modifier.padding() |
| android:layout_margin | Modifier.padding() on parent, or use Arrangement.spacedBy() |
| android:background | Modifier.background() |
| android:visibility="gone" | Conditional composition (don't emit) |
| android:visibility="invisible" | Modifier.alpha(0f) (keeps space) |
| android:clickable | Modifier.clickable { } |
| android:contentDescription | Modifier.semantics { contentDescription = "" } |
| android:elevation | Modifier.shadow() or component's elevation param |
| android:alpha | Modifier.alpha() |
| android:rotation | Modifier.rotate() |
| android:scaleX/Y | Modifier.scale() |
| android:gravity | Alignment parameter or Arrangement |
| android:layout_gravity | Modifier.align() |
<!-- XML -->
<LinearLayout android:orientation="horizontal">
<View android:layout_weight="1" />
<View android:layout_weight="2" />
</LinearLayout>
// Compose
Row(modifier = Modifier.fillMaxWidth()) {
Box(modifier = Modifier.weight(1f))
Box(modifier = Modifier.weight(2f))
}
<!-- XML -->
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
// Compose
LazyColumn(modifier = Modifier.fillMaxSize()) {
items(items, key = { it.id }) { item ->
ItemRow(item = item, onClick = { onItemClick(item) })
}
}
<!-- XML with Data Binding -->
<EditText
android:text="@={viewModel.username}"
android:hint="@string/username_hint" />
// Compose
val username by viewModel.username.collectAsState()
OutlinedTextField(
value = username,
onValueChange = { viewModel.updateUsername(it) },
label = { Text(stringResource(R.string.username_hint)) },
modifier = Modifier.fillMaxWidth()
)
<!-- XML -->
<androidx.constraintlayout.widget.ConstraintLayout>
<TextView
android:id="@+id/title"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<TextView
android:id="@+id/subtitle"
app:layout_constraintTop_toBottomOf="@id/title"
app:layout_constraintStart_toStartOf="@id/title" />
</androidx.constraintlayout.widget.ConstraintLayout>
// Compose
ConstraintLayout(modifier = Modifier.fillMaxWidth()) {
val (title, subtitle) = createRefs()
Text(
text = "Title",
modifier = Modifier.constrainAs(title) {
top.linkTo(parent.top)
start.linkTo(parent.start)
}
)
Text(
text = "Subtitle",
modifier = Modifier.constrainAs(subtitle) {
top.linkTo(title.bottom)
start.linkTo(title.start)
}
)
}
<!-- XML: layout_header.xml -->
<merge>
<ImageView android:id="@+id/avatar" />
<TextView android:id="@+id/name" />
</merge>
<!-- Usage -->
<include layout="@layout/layout_header" />
// Compose: Extract as a reusable Composable
@Composable
fun HeaderSection(
avatarUrl: String,
name: String,
modifier: Modifier = Modifier
) {
Row(modifier = modifier) {
AsyncImage(model = avatarUrl, contentDescription = null)
Text(text = name)
}
}
// Usage
HeaderSection(avatarUrl = user.avatar, name = user.name)
<!-- In your XML layout -->
<androidx.compose.ui.platform.ComposeView
android:id="@+id/compose_view"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
// In Fragment/Activity
binding.composeView.setContent {
MaterialTheme {
MyComposable()
}
}
// Use AndroidView for Views that don't have Compose equivalents
@Composable
fun MapViewComposable(modifier: Modifier = Modifier) {
AndroidView(
factory = { context ->
MapView(context).apply {
// Initialize the view
}
},
update = { mapView ->
// Update the view when state changes
},
modifier = modifier
)
}
// Before: Observing in Fragment
viewModel.uiState.observe(viewLifecycleOwner) { state ->
binding.title.text = state.title
}
// After: Collecting in Compose
@Composable
fun MyScreen(viewModel: MyViewModel = koinViewModel()) {
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
Text(text = uiState.title)
}
// Before: XML + setOnClickListener
binding.submitButton.setOnClickListener {
viewModel.submit()
}
// After: Lambda in Compose
Button(onClick = { viewModel.submit() }) {
Text("Submit")
}
include or merge left)development
Apply Shadmin feature-development standards (backend Go/Gin/Ent + frontend React/TS). Use when adding/modifying features, CRUD modules, API routes/controllers/usecases/repositories, Ent schemas, or web pages/routes.
development
Kotlin Coroutines review and remediation for Android. Use when asked to review concurrency usage, fix coroutine-related bugs, improve thread safety, or resolve lifecycle issues in Kotlin/Android code.
development
Debug and optimize Android/Gradle build performance. Use when builds are slow, investigating CI/CD performance, analyzing build scans, or identifying compilation bottlenecks.
development
Best practices for building UI with Jetpack Compose, focusing on state hoisting, detailed performance optimizations, and theming. Use this when writing or refactoring Composable functions.