{"id":"c4b20434-96c1-4a18-a1d5-85b84ee2bf23","shortId":"vmyN7U","kind":"skill","title":"compose-ui","tagline":"Jetpack Compose UI best practices for AI agents building Android apps. Use this skill\nwhenever writing any Composable function, building screens, handling UI state, working with\nScaffold, LazyColumn, ModalBottomSheet, BottomSheet, edge-to-edge, IME keyboard insets,\nrecomposition,","description":"# Compose UI\n\n24 rules that fix what AI agents consistently get wrong in Jetpack Compose.\n\n## CRITICAL rules — get these wrong and the app is broken\n\n### 1. Edge-to-edge + Scaffold innerPadding\n\n```kotlin\n// ✅ Always consume Scaffold's innerPadding\nScaffold(\n    topBar = { TopAppBar(title = { Text(\"Screen\") }) },\n    bottomBar = { BottomNavBar() }\n) { innerPadding ->\n    LazyColumn(\n        modifier = Modifier.fillMaxSize(),\n        contentPadding = innerPadding   // ← pass to content, never ignore\n    ) { ... }\n}\n\n// ❌ Never ignore innerPadding — content hides under bars\nScaffold { _ ->\n    LazyColumn { ... }  // content hidden under top/bottom bars\n}\n```\n\n### 2. ModalBottomSheet navigation bar padding\n\n```kotlin\n// ✅ Always add navigationBarsPadding inside BottomSheet content\nModalBottomSheet(onDismissRequest = { ... }) {\n    Column(\n        modifier = Modifier\n            .fillMaxWidth()\n            .navigationBarsPadding()   // ← required, content shifts above nav bar\n            .padding(16.dp)\n    ) { ... }\n}\n\n// ❌ Missing navigationBarsPadding — content hidden behind gesture nav bar\nModalBottomSheet(onDismissRequest = { ... }) {\n    Column(modifier = Modifier.padding(16.dp)) { ... }\n}\n```\n\n### 3. Single UiState sealed class per screen\n\n```kotlin\n// ✅ One sealed class represents all screen states\nsealed interface HomeUiState {\n    data object Loading : HomeUiState\n    data class Success(val items: List<Item>) : HomeUiState\n    data class Error(val message: String) : HomeUiState\n    data object Empty : HomeUiState\n}\n\n// ✅ Always handle all states in the Composable\n@Composable\nfun HomeScreen(uiState: HomeUiState) {\n    when (uiState) {\n        is HomeUiState.Loading -> LoadingIndicator()\n        is HomeUiState.Success -> ItemList(uiState.items)\n        is HomeUiState.Error -> ErrorMessage(uiState.message)\n        is HomeUiState.Empty -> EmptyState()\n    }\n}\n\n// ❌ Never use multiple booleans for state — causes impossible states\ndata class HomeState(\n    val isLoading: Boolean = false,\n    val isError: Boolean = false,\n    val isEmpty: Boolean = false,\n    val items: List<Item> = emptyList()\n)\n```\n\n### 4. collectAsStateWithLifecycle — never collectAsState\n\n```kotlin\n// ✅ Lifecycle-aware collection — pauses when app is in background\nval uiState by viewModel.uiState.collectAsStateWithLifecycle()\n\n// ❌ collectAsState — keeps collecting even when app is backgrounded\nval uiState by viewModel.uiState.collectAsState()\n```\n\n### 5. SharedFlow for one-shot events\n\n```kotlin\n// ✅ SharedFlow for navigation events, toasts, dialogs\nclass HomeViewModel : ViewModel() {\n    private val _events = MutableSharedFlow<HomeEvent>()\n    val events: SharedFlow<HomeEvent> = _events.asSharedFlow()\n\n    fun onItemClick(id: String) {\n        viewModelScope.launch {\n            _events.emit(HomeEvent.NavigateToDetail(id))\n        }\n    }\n}\n\n// ✅ Collect events with LaunchedEffect\nLaunchedEffect(Unit) {\n    viewModel.events.collect { event ->\n        when (event) {\n            is HomeEvent.NavigateToDetail -> navController.navigate(\"detail/${event.id}\")\n        }\n    }\n}\n\n// ❌ Never use StateFlow for one-shot events — events replay on recomposition\nprivate val _navigationEvent = MutableStateFlow<String?>(null)\n```\n\n### 6. LaunchedEffect key rules\n\n```kotlin\n// ✅ Use Unit key when effect runs once on composition\nLaunchedEffect(Unit) {\n    viewModel.loadData()\n}\n\n// ✅ Use value key when effect should rerun when value changes\nLaunchedEffect(userId) {\n    viewModel.loadUser(userId)\n}\n\n// ❌ Never use constantly-changing keys — causes infinite re-execution\nLaunchedEffect(System.currentTimeMillis()) { ... }\n```\n\n### 7. No logic in composition — only rendering\n\n```kotlin\n// ✅ Logic in ViewModel, only UI state in Composable\n@Composable\nfun ItemCard(item: Item, onLike: (String) -> Unit) {\n    Card(onClick = { onLike(item.id) }) {\n        Text(item.title)\n    }\n}\n\n// ❌ Never compute in composition — triggers recomposition loops\n@Composable\nfun ItemCard(items: List<Item>) {\n    val filtered = items.filter { it.isActive }  // computation in composition!\n    ...\n}\n```\n\n## HIGH impact rules\n\n### 8. LazyColumn — stable keys and contentPadding\n\n```kotlin\n// ✅ Always provide stable key= for list items\nLazyColumn(\n    contentPadding = PaddingValues(16.dp),\n    verticalArrangement = Arrangement.spacedBy(8.dp)\n) {\n    items(items, key = { it.id }) { item ->   // ← stable key prevents recompose\n        ItemCard(item)\n    }\n}\n\n// ❌ No key — full list recomposition on any change\nLazyColumn { items(items) { item -> ItemCard(item) } }\n```\n\n### 9. Modifier order matters\n\n```kotlin\n// ✅ Correct order: size → padding → background → clickable\nModifier\n    .fillMaxWidth()\n    .padding(16.dp)\n    .background(MaterialTheme.colorScheme.surface)\n    .clickable { ... }\n\n// ❌ Wrong order changes visual result and hit area\nModifier\n    .clickable { ... }         // clickable area doesn't include padding\n    .padding(16.dp)\n    .fillMaxWidth()\n```\n\n### 10. AnimatedVisibility — always specify enter/exit\n\n```kotlin\n// ✅ Explicit animation specs\nAnimatedVisibility(\n    visible = isVisible,\n    enter = fadeIn() + expandVertically(),\n    exit = fadeOut() + shrinkVertically()\n) { Content() }\n\n// ❌ Default enter/exit looks janky\nAnimatedVisibility(visible = isVisible) { Content() }\n```\n\n### 11. Type-safe Navigation destinations\n\n```kotlin\n// ✅ Kotlin Serializable destinations — no string routes\n@Serializable\nobject HomeRoute\n\n@Serializable\ndata class DetailRoute(val itemId: String)\n\nNavHost(navController, startDestination = HomeRoute) {\n    composable<HomeRoute> { HomeScreen() }\n    composable<DetailRoute> { backStackEntry ->\n        val route: DetailRoute = backStackEntry.toRoute()\n        DetailScreen(itemId = route.itemId)\n    }\n}\n\n// ❌ String routes — fragile, no type safety, typos compile\nnavController.navigate(\"detail/$itemId\")\ncomposable(\"detail/{itemId}\") { ... }\n```\n\n### 12. Remember variants — use the right one\n\n```kotlin\n// ✅ remember for values that survive recomposition but not config change\nval scrollState = rememberScrollState()\n\n// ✅ rememberSaveable for values that survive config change (rotation)\nvar selectedTab by rememberSaveable { mutableStateOf(0) }\n\n// ✅ remember with key — recalculate when key changes\nval computation = remember(inputList) { inputList.sortedBy { it.name } }\n\n// ❌ remember without key for derived state — stale value\nval sorted = remember { inputList.sortedBy { it.name } }  // never updates\n```\n\n### 13. DisposableEffect for cleanup\n\n```kotlin\n// ✅ DisposableEffect cleans up when composable leaves\nDisposableEffect(lifecycleOwner) {\n    val observer = LifecycleEventObserver { _, event ->\n        if (event == Lifecycle.Event.ON_RESUME) viewModel.refreshData()\n    }\n    lifecycleOwner.lifecycle.addObserver(observer)\n    onDispose { lifecycleOwner.lifecycle.removeObserver(observer) }\n}\n```\n\n### 14. Accessibility semantics\n\n```kotlin\n// ✅ Custom actions and descriptions for non-text clickables\nIconButton(\n    onClick = { onLike() },\n    modifier = Modifier.semantics {\n        contentDescription = \"Like ${item.title}\"\n        role = Role.Button\n    }\n) { Icon(Icons.Default.Favorite, contentDescription = null) }\n\n// ✅ mergeDescendants for card accessibility\nCard(\n    modifier = Modifier.semantics(mergeDescendants = true) {}\n) { ... }\n```\n\n### 15. TextField security\n\n```kotlin\n// ✅ Password field with correct input type\nTextField(\n    value = password,\n    onValueChange = { password = it },\n    visualTransformation = PasswordVisualTransformation(),\n    keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password),\n    keyboardActions = KeyboardActions(onDone = { onLogin() })\n)\n```\n\n## MEDIUM impact rules\n\n### 16. Multi-preview annotations\n\n```kotlin\n// ✅ Test across phones, large screens, and dark mode\n@Preview(name = \"Phone\", device = Devices.PHONE)\n@Preview(name = \"Tablet\", device = Devices.TABLET)\n@Preview(name = \"Dark\", uiMode = Configuration.UI_MODE_NIGHT_YES)\n@Composable\nfun HomeScreenPreview() {\n    MyAppTheme { HomeScreen(uiState = HomeUiState.Success(sampleItems)) }\n}\n```\n\n### 17. Material 3 — no hardcoded colors\n\n```kotlin\n// ✅ Always use theme color roles\nText(text = title, color = MaterialTheme.colorScheme.onSurface)\nCard(colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.surfaceVariant))\n\n// ❌ Never hardcode colors — breaks dark mode and dynamic color\nText(text = title, color = Color(0xFF333333))\n```\n\n### 18. Coil image loading\n\n```kotlin\n// ✅ AsyncImage with contentScale and placeholder\nAsyncImage(\n    model = ImageRequest.Builder(LocalContext.current)\n        .data(url)\n        .crossfade(true)\n        .build(),\n    contentDescription = description,\n    contentScale = ContentScale.Crop,\n    placeholder = painterResource(R.drawable.placeholder),\n    error = painterResource(R.drawable.error_image),\n    modifier = Modifier.fillMaxWidth().aspectRatio(16f/9f)\n)\n```\n\n### 19. WindowInsets — IME keyboard handling\n\n```kotlin\n// ✅ Shift content above keyboard automatically\nScaffold(\n    modifier = Modifier.imePadding()\n) { innerPadding ->\n    Column(modifier = Modifier.padding(innerPadding)) {\n        TextField(value = text, onValueChange = { text = it })\n    }\n}\n```\n\n### 20. Runtime permissions in Compose\n\n```kotlin\n// ✅ rememberPermissionState from Accompanist/Compose Permissions\nval cameraPermission = rememberPermissionState(Manifest.permission.CAMERA)\n\nLaunchedEffect(Unit) {\n    if (!cameraPermission.status.isGranted) {\n        cameraPermission.launchPermissionRequest()\n    }\n}\n```\n\n### 21. Scaffold with FAB and SnackbarHost\n\n```kotlin\n// ✅ Complete Scaffold setup\nval snackbarHostState = remember { SnackbarHostState() }\n\nScaffold(\n    topBar = { TopAppBar(title = { Text(\"Home\") }) },\n    floatingActionButton = {\n        FloatingActionButton(onClick = onCreate) {\n            Icon(Icons.Default.Add, contentDescription = \"Create\")\n        }\n    },\n    snackbarHost = { SnackbarHost(snackbarHostState) }\n) { innerPadding ->\n    Content(modifier = Modifier.padding(innerPadding))\n}\n```\n\n### 22. Surface vs Box — use Surface for clickable containers\n\n```kotlin\n// ✅ Surface handles elevation, ripple, shape, and color role\nSurface(\n    onClick = { onItemClick(item.id) },\n    shape = MaterialTheme.shapes.medium,\n    tonalElevation = 2.dp\n) { CardContent(item) }\n\n// ❌ Box has no elevation or ripple semantics\nBox(modifier = Modifier.clickable { onItemClick(item.id) }) { CardContent(item) }\n```\n\n### 23. Stateless Composables — hoist state up\n\n```kotlin\n// ✅ Stateless composable — testable and reusable\n@Composable\nfun SearchBar(\n    query: String,\n    onQueryChange: (String) -> Unit,\n    modifier: Modifier = Modifier\n) {\n    TextField(value = query, onValueChange = onQueryChange, modifier = modifier)\n}\n\n// ❌ Stateful — owns state internally, hard to test, not reusable\n@Composable\nfun SearchBar() {\n    var query by remember { mutableStateOf(\"\") }\n    TextField(value = query, onValueChange = { query = it })\n}\n```\n\n### 24. Adaptive layout with WindowSizeClass\n\n```kotlin\n// ✅ Respond to window size — see adaptive-ui skill for full rules\nval windowSizeClass = calculateWindowSizeClass(activity)\nwhen (windowSizeClass.widthSizeClass) {\n    WindowWidthSizeClass.Compact -> PhoneLayout()\n    WindowWidthSizeClass.Medium -> TabletLayout()\n    WindowWidthSizeClass.Expanded -> DesktopLayout()\n}\n```\n\n## Common Mistakes (Quick Reference)\n\n| ❌ Wrong | ✅ Right |\n|---|---|\n| `collectAsState()` | `collectAsStateWithLifecycle()` |\n| Ignoring `innerPadding` | `contentPadding = innerPadding` |\n| Multiple state booleans | Single `sealed interface UiState` |\n| String routes | `@Serializable` route objects |\n| Hardcoded colors | `MaterialTheme.colorScheme.*` |\n| Logic in `@Composable` | Logic in ViewModel |\n| StateFlow for events | SharedFlow for events |\n| No `key=` in LazyColumn | `items(list, key = { it.id })` |\n\n## Deep-dive references\n\n- `references/side-effects.md` — complete LaunchedEffect / DisposableEffect / SideEffect guide\n- `references/state-management.md` — derivedStateOf, snapshotFlow, state in multi-module apps\n- `references/compose-performance.md` — stability, skippable composables, baseline profiles","tags":["compose","android","agent","skills","piyushverma0","agent-skills","ai-agent","antigravity","claude-code","codex","cursor","gemini-cli"],"capabilities":["skill","source-piyushverma0","skill-compose-ui","topic-agent-skills","topic-ai-agent","topic-android","topic-antigravity","topic-claude-code","topic-codex","topic-cursor","topic-gemini-cli","topic-hilt","topic-jetpack-compose","topic-kotlin","topic-material3"],"categories":["android-agent-skills"],"synonyms":[],"warnings":[],"endpointUrl":"https://skills.sh/piyushverma0/android-agent-skills/compose-ui","protocol":"skill","transport":"skills-sh","auth":{"type":"none","details":{"cli":"npx skills add piyushverma0/android-agent-skills","source_repo":"https://github.com/piyushverma0/android-agent-skills","install_from":"skills.sh"}},"qualityScore":"0.454","qualityRationale":"deterministic score 0.45 from registry signals: · indexed on github topic:agent-skills · 8 github stars · SKILL.md body (12,205 chars)","verified":false,"liveness":"unknown","lastLivenessCheck":null,"agentReviews":{"count":0,"score_avg":null,"cost_usd_avg":null,"success_rate":null,"latency_p50_ms":null,"narrative_summary":null,"summary_updated_at":null},"enrichmentModel":"deterministic:skill-github:v1","enrichmentVersion":1,"enrichedAt":"2026-05-18T19:09:09.360Z","embedding":null,"createdAt":"2026-05-18T13:14:48.937Z","updatedAt":"2026-05-18T19:09:09.360Z","lastSeenAt":"2026-05-18T19:09:09.360Z","tsv":"'0':639 '0xff333333':836 '1':67 '10':526 '11':553 '12':605 '13':668 '14':695 '15':731 '16':760 '16.dp':139,153,460,503,524 '16f/9f':870 '17':800 '18':837 '19':871 '2':113 '2.dp':976 '20':896 '21':915 '22':951 '23':993 '24':44,1046 '3':154,802 '4':250 '5':281 '6':347 '7':391 '8':443 '8.dp':463 '9':489 'access':696,725 'accompanist/compose':904 'across':767 'action':700 'activ':1067 'adapt':1047,1058 'adaptive-ui':1057 'add':120 'agent':11,50 'ai':10,49 'alway':75,119,194,450,528,807 'android':13 'anim':533 'animatedvis':527,535,549 'annot':764 'app':14,64,261,274,1141 'area':514,518 'arrangement.spacedby':462 'aspectratio':869 'asyncimag':842,847 'automat':881 'awar':257 'background':264,276,498,504 'backstackentri':583 'backstackentry.toroute':587 'bar':105,112,116,137,147 'baselin':1146 'behind':144 'best':7 'boolean':225,236,240,244,1090 'bottombar':86 'bottomnavbar':87 'bottomsheet':33,123 'box':954,979,986 'break':825 'broken':66 'build':12,23,855 'calculatewindowsizeclass':1066 'camerapermiss':907 'camerapermission.launchpermissionrequest':914 'camerapermission.status.isgranted':913 'card':415,724,726,817 'cardcont':977,991 'carddefaults.cardcolors':819 'caus':228,384 'chang':373,382,482,509,622,632,646 'class':158,164,177,184,232,295,571 'clean':674 'cleanup':671 'clickabl':499,506,516,517,707,958 'coil':838 'collect':258,271,314 'collectasst':253,269,1082 'collectasstatewithlifecycl':251,1083 'color':805,810,815,818,824,830,834,835,967,1101 'column':127,150,886 'common':1076 'compil':598 'complet':922,1128 'compos':2,5,21,42,56,200,201,406,407,428,580,582,602,677,792,900,995,1001,1005,1032,1105,1145 'compose-ui':1 'composit':360,395,424,439 'comput':422,437,648 'config':621,631 'configuration.ui':788 'consist':51 'constant':381 'constantly-chang':380 'consum':76 'contain':959 'containercolor':820 'content':96,102,108,124,133,142,544,552,878,947 'contentdescript':713,720,856,941 'contentpad':92,448,458,1086 'contentscal':844,858 'contentscale.crop':859 'correct':494,738 'creat':942 'critic':57 'crossfad':853 'custom':699 'dark':772,786,826 'data':172,176,183,190,231,570,851 'deep':1124 'deep-div':1123 'default':545 'deriv':657 'derivedstateof':1134 'descript':702,857 'desktoplayout':1075 'destin':558,562 'detail':327,600,603 'detailrout':572,586 'detailscreen':588 'devic':777,782 'devices.phone':778 'devices.tablet':783 'dialog':294 'disposableeffect':669,673,679,1130 'dive':1125 'doesn':519 'dynam':829 'edg':35,37,69,71 'edge-to-edg':34,68 'effect':356,368 'elev':963,982 'empti':192 'emptylist':249 'emptyst':221 'enter':538 'enter/exit':530,546 'error':185,863 'errormessag':217 'even':272 'event':287,292,300,303,315,321,323,336,337,684,686,1111,1114 'event.id':328 'events.assharedflow':305 'events.emit':311 'execut':388 'exit':541 'expandvert':540 'explicit':532 'fab':918 'fadein':539 'fadeout':542 'fals':237,241,245 'field':736 'fillmaxwidth':130,501,525 'filter':434 'fix':47 'floatingactionbutton':935,936 'fragil':593 'full':477,1062 'fun':202,306,408,429,793,1006,1033 'function':22 'gestur':145 'get':52,59 'guid':1132 'handl':25,195,875,962 'hard':1027 'hardcod':804,823,1100 'hidden':109,143 'hide':103 'high':440 'hit':513 'hoist':996 'home':934 'homeevent.navigatetodetail':312,325 'homerout':568,579 'homescreen':203,581,796 'homescreenpreview':794 'homest':233 'homeuist':171,175,182,189,193,205 'homeuistate.empty':220 'homeuistate.error':216 'homeuistate.loading':209 'homeuistate.success':212,798 'homeviewmodel':296 'icon':718,939 'iconbutton':708 'icons.default.add':940 'icons.default.favorite':719 'id':308,313 'ignor':98,100,1084 'imag':839,866 'imagerequest.builder':849 'ime':38,873 'impact':441,758 'imposs':229 'includ':521 'infinit':385 'innerpad':73,79,88,93,101,885,889,946,950,1085,1087 'input':739 'inputlist':650 'inputlist.sortedby':651,664 'inset':40 'insid':122 'interfac':170,1093 'intern':1026 'isempti':243 'iserror':239 'isload':235 'isvis':537,551 'it.id':467,1122 'it.isactive':436 'it.name':652,665 'item':180,247,410,411,431,456,464,465,468,474,484,485,486,488,978,992,1119 'item.id':418,972,990 'item.title':420,715 'itemcard':409,430,473,487 'itemid':574,589,601,604 'itemlist':213 'items.filter':435 'janki':548 'jetpack':4,55 'keep':270 'key':349,354,366,383,446,453,466,470,476,642,645,655,1116,1121 'keyboard':39,874,880 'keyboardact':753,754 'keyboardopt':749,750 'keyboardtyp':751 'keyboardtype.password':752 'kotlin':74,118,161,254,288,351,398,449,493,531,559,560,612,672,698,734,765,806,841,876,901,921,960,999,1051 'larg':769 'launchedeffect':317,318,348,361,374,389,910,1129 'layout':1048 'lazycolumn':31,89,107,444,457,483,1118 'leav':678 'lifecycl':256 'lifecycle-awar':255 'lifecycle.event.on':687 'lifecycleeventobserv':683 'lifecycleown':680 'lifecycleowner.lifecycle.addobserver':690 'lifecycleowner.lifecycle.removeobserver':693 'like':714 'list':181,248,432,455,478,1120 'load':174,840 'loadingind':210 'localcontext.current':850 'logic':393,399,1103,1106 'look':547 'loop':427 'manifest.permission.camera':909 'materi':801 'materialtheme.colorscheme':1102 'materialtheme.colorscheme.onsurface':816 'materialtheme.colorscheme.surface':505 'materialtheme.colorscheme.surfacevariant':821 'materialtheme.shapes.medium':974 'matter':492 'medium':757 'mergedescend':722,729 'messag':187 'miss':140 'mistak':1077 'modalbottomsheet':32,114,125,148 'mode':773,789,827 'model':848 'modifi':90,128,129,151,490,500,515,711,727,867,883,887,948,987,1013,1014,1015,1021,1022 'modifier.clickable':988 'modifier.fillmaxsize':91 'modifier.fillmaxwidth':868 'modifier.imepadding':884 'modifier.padding':152,888,949 'modifier.semantics':712,728 'modul':1140 'multi':762,1139 'multi-modul':1138 'multi-preview':761 'multipl':224,1088 'mutablesharedflow':301 'mutablestateflow':344 'mutablestateof':638,1039 'myappthem':795 'name':775,780,785 'nav':136,146 'navcontrol':577 'navcontroller.navigate':326,599 'navhost':576 'navig':115,291,557 'navigationbarspad':121,131,141 'navigationev':343 'never':97,99,222,252,329,378,421,666,822 'night':790 'non':705 'non-text':704 'null':346,721 'object':173,191,567,1099 'observ':682,691,694 'onclick':416,709,937,970 'oncreat':938 'ondismissrequest':126,149 'ondispos':692 'ondon':755 'one':162,285,334,611 'one-shot':284,333 'onitemclick':307,971,989 'onlik':412,417,710 'onlogin':756 'onquerychang':1010,1020 'onvaluechang':744,893,1019,1043 'order':491,495,508 'own':1024 'pad':117,138,497,502,522,523 'paddingvalu':459 'painterresourc':861,864 'pass':94 'password':735,743,745 'passwordvisualtransform':748 'paus':259 'per':159 'permiss':898,905 'phone':768,776 'phonelayout':1071 'placehold':846,860 'practic':8 'prevent':471 'preview':763,774,779,784 'privat':298,341 'profil':1147 'provid':451 'queri':1008,1018,1036,1042,1044 'quick':1078 'r.drawable.error':865 'r.drawable.placeholder':862 're':387 're-execut':386 'recalcul':643 'recompos':472 'recomposit':41,340,426,479,618 'refer':1079,1126 'references/compose-performance.md':1142 'references/side-effects.md':1127 'references/state-management.md':1133 'rememb':606,613,640,649,653,663,927,1038 'rememberpermissionst':902,908 'remembersav':626,637 'rememberscrollst':625 'render':397 'replay':338 'repres':165 'requir':132 'rerun':370 'respond':1052 'result':511 'resum':688 'reusabl':1004,1031 'right':610,1081 'rippl':964,984 'role':716,811,968 'role.button':717 'rotat':633 'rout':565,585,592,1096,1098 'route.itemid':590 'rule':45,58,350,442,759,1063 'run':357 'runtim':897 'safe':556 'safeti':596 'sampleitem':799 'scaffold':30,72,77,80,106,882,916,923,929 'screen':24,85,160,167,770 'scrollstat':624 'seal':157,163,169,1092 'searchbar':1007,1034 'secur':733 'see':1056 'selectedtab':635 'semant':697,985 'serializ':561,566,569,1097 'setup':924 'shape':965,973 'sharedflow':282,289,304,1112 'shift':134,877 'shot':286,335 'shrinkvert':543 'sideeffect':1131 'singl':155,1091 'size':496,1055 'skill':17,1060 'skill-compose-ui' 'skippabl':1144 'snackbarhost':920,943,944 'snackbarhostst':926,928,945 'snapshotflow':1135 'sort':662 'source-piyushverma0' 'spec':534 'specifi':529 'stabil':1143 'stabl':445,452,469 'stale':659 'startdestin':578 'state':27,168,197,227,230,404,658,997,1023,1025,1089,1136 'stateflow':331,1109 'stateless':994,1000 'string':188,309,345,413,564,575,591,1009,1011,1095 'success':178 'surfac':952,956,961,969 'surviv':617,630 'system.currenttimemillis':390 'tablet':781 'tabletlayout':1073 'test':766,1029 'testabl':1002 'text':84,419,706,812,813,831,832,892,894,933 'textfield':732,741,890,1016,1040 'theme':809 'titl':83,814,833,932 'toast':293 'tonalelev':975 'top/bottom':111 'topappbar':82,931 'topbar':81,930 'topic-agent-skills' 'topic-ai-agent' 'topic-android' 'topic-antigravity' 'topic-claude-code' 'topic-codex' 'topic-cursor' 'topic-gemini-cli' 'topic-hilt' 'topic-jetpack-compose' 'topic-kotlin' 'topic-material3' 'trigger':425 'true':730,854 'type':555,595,740 'type-saf':554 'typo':597 'ui':3,6,26,43,403,1059 'uimod':787 'uistat':156,204,207,266,278,797,1094 'uistate.items':214 'uistate.message':218 'unit':319,353,362,414,911,1012 'updat':667 'url':852 'use':15,223,330,352,364,379,608,808,955 'userid':375,377 'val':179,186,234,238,242,246,265,277,299,302,342,433,573,584,623,647,661,681,906,925,1064 'valu':365,372,615,628,660,742,891,1017,1041 'var':634,1035 'variant':607 'verticalarrang':461 'viewmodel':297,401,1108 'viewmodel.events.collect':320 'viewmodel.loaddata':363 'viewmodel.loaduser':376 'viewmodel.refreshdata':689 'viewmodel.uistate.collectasstate':280 'viewmodel.uistate.collectasstatewithlifecycle':268 'viewmodelscope.launch':310 'visibl':536,550 'visual':510 'visualtransform':747 'vs':953 'whenev':18 'window':1054 'windowinset':872 'windowsizeclass':1050,1065 'windowsizeclass.widthsizeclass':1069 'windowwidthsizeclass.compact':1070 'windowwidthsizeclass.expanded':1074 'windowwidthsizeclass.medium':1072 'without':654 'work':28 'write':19 'wrong':53,61,507,1080 'yes':791","prices":[{"id":"e279e402-41f9-4644-a8e5-2fe86987e0c1","listingId":"c4b20434-96c1-4a18-a1d5-85b84ee2bf23","amountUsd":"0","unit":"free","nativeCurrency":null,"nativeAmount":null,"chain":null,"payTo":null,"paymentMethod":"skill-free","isPrimary":true,"details":{"org":"piyushverma0","category":"android-agent-skills","install_from":"skills.sh"},"createdAt":"2026-05-18T13:14:48.937Z"}],"sources":[{"listingId":"c4b20434-96c1-4a18-a1d5-85b84ee2bf23","source":"github","sourceId":"piyushverma0/android-agent-skills/compose-ui","sourceUrl":"https://github.com/piyushverma0/android-agent-skills/tree/main/skills/compose-ui","isPrimary":false,"firstSeenAt":"2026-05-18T13:14:48.937Z","lastSeenAt":"2026-05-18T19:09:09.360Z"}],"details":{"listingId":"c4b20434-96c1-4a18-a1d5-85b84ee2bf23","quickStartSnippet":null,"exampleRequest":null,"exampleResponse":null,"schema":null,"openapiUrl":null,"agentsTxtUrl":null,"citations":[],"useCases":[],"bestFor":[],"notFor":[],"kindDetails":{"org":"piyushverma0","slug":"compose-ui","github":{"repo":"piyushverma0/android-agent-skills","stars":8,"topics":["agent-skills","ai-agent","android","antigravity","claude-code","codex","cursor","gemini-cli","hilt","jetpack-compose","kotlin","material3","open-source","skills","supabase"],"license":"mit","html_url":"https://github.com/piyushverma0/android-agent-skills","pushed_at":"2026-04-27T09:15:31Z","description":"27 Android skills for AI agents (Claude Code, Codex, Cursor). Fixes Supabase auth, Hilt errors, design inconsistency, kapt→ksp, missing UiState states. Reduced my token bills 5×. FitGenZ AI shipped in 18 days.","skill_md_sha":"0f32c845dec8b87fd33dc20e40fd46d3618b2cc7","skill_md_path":"skills/compose-ui/SKILL.md","default_branch":"main","skill_tree_url":"https://github.com/piyushverma0/android-agent-skills/tree/main/skills/compose-ui"},"layout":"multi","source":"github","category":"android-agent-skills","frontmatter":{"name":"compose-ui","description":"Jetpack Compose UI best practices for AI agents building Android apps. Use this skill\nwhenever writing any Composable function, building screens, handling UI state, working with\nScaffold, LazyColumn, ModalBottomSheet, BottomSheet, edge-to-edge, IME keyboard insets,\nrecomposition, side effects, LaunchedEffect, DisposableEffect, remember, collectAsStateWithLifecycle,\nStateFlow, SharedFlow, UiState, loading/error/empty states, accessibility semantics,\nMaterial 3 components, Coil images, TextField, animations, modifiers, or any Compose layout.\nAlways apply this skill before writing any @Composable function."},"skills_sh_url":"https://skills.sh/piyushverma0/android-agent-skills/compose-ui"},"updatedAt":"2026-05-18T19:09:09.360Z"}}