Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Expert skill for native Android development with Kotlin and Jetpack Compose
.claude/skills/a5c-ai-kotlin-jetpack-compose-development/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-08 | ✗→✓ | ▲ Improved | 219% | 0% |
| case-16 | ✗→✓ | ▲ Improved | 93% | 0% |
| case-01 | ✓→✓ | = Same ✓ | 232% | 0% |
| case-02 | ✓→✓ | = Same ✓ | 179% | 0% |
| case-03 | ✓→✓ | = Same ✓ | 223% | 0% |
This skill provides expert capabilities for native Android development using Kotlin and Jetpack Compose. It enables generation of Compose UI components, implementation of modern Android architecture patterns, and comprehensive Gradle build operations.
bash - Execute Gradle commands, adb, and Android SDK toolsread - Analyze Kotlin source files and Gradle configurationswrite - Generate and modify Kotlin code and Compose composablesedit - Update existing Kotlin code and configurationsglob - Search for Kotlin files and Android resourcesgrep - Search for patterns in Android codebaseThis skill integrates with the following processes:
jetpack-compose-ui.js - Compose UI developmentandroid-room-database.js - Room persistencefirebase-cloud-messaging.js - FCM integrationandroid-playstore-publishing.js - Play Store submissionapp/
├── src/
│ ├── main/
│ │ ├── kotlin/com/example/myapp/
│ │ │ ├── MyApplication.kt
│ │ │ ├── MainActivity.kt
│ │ │ ├── di/
│ │ │ │ └── AppModule.kt
│ │ │ ├── data/
│ │ │ │ ├── repository/
│ │ │ │ ├── local/
│ │ │ │ └── remote/
│ │ │ ├── domain/
│ │ │ │ ├── model/
│ │ │ │ ├── repository/
│ │ │ │ └── usecase/
│ │ │ └── ui/
│ │ │ ├── theme/
│ │ │ ├── navigation/
│ │ │ └── feature/
│ │ ├── res/
│ │ └── AndroidManifest.xml
│ ├── test/
│ └── androidTest/
├── build.gradle.kts
└── proguard-rules.protoml# gradle/libs.versions.toml [versions] kotlin = "1.9.21" compose-bom = "2024.01.00" hilt = "2.50" room = "2.6.1" lifecycle = "2.7.0" [libraries] compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "compose-bom" } compose-ui = { group = "androidx.compose.ui", name = "ui" } compose-material3 = { group = "androidx.compose.material3", name = "material3" } compose-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview" } hilt-android = { group = "com.google.dagger", name = "hilt-android", version.ref = "hilt" } hilt-compiler = { group = "com.google.dagger", name = "hilt-compiler", version.ref = "hilt" } room-runtime = { group = "androidx.room", name = "room-runtime", version.ref = "room" } room-ktx = { group = "androidx.room", name = "room-ktx", version.ref = "room" } room-compiler = { group = "androidx.room", name = "room-compiler", version.ref = "room" } [plugins] android-application = { id = "com.android.application", version = "8.2.0" } kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } hilt = { id = "com.google.dagger.hilt.android", version.ref = "hilt" } ksp = { id = "com.google.devtools.ksp", version = "1.9.21-1.0.16" }
kotlin// ui/feature/home/HomeScreen.kt package com.example.myapp.ui.feature.home import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.material3.* import androidx.compose.runtime.* import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel @OptIn(ExperimentalMaterial3Api::class) @Composable fun HomeScreen( viewModel: HomeViewModel = hiltViewModel(), onItemClick: (String) -> Unit ) { val uiState by viewModel.uiState.collectAsStateWithLifecycle() Scaffold( topBar = { TopAppBar( title = { Text("Home") } ) } ) { paddingValues -> when (val state = uiState) { is HomeUiState.Loading -> { Box( modifier = Modifier .fillMaxSize() .padding(paddingValues), contentAlignment = Alignment.Center ) { CircularProgressIndicator() } } is HomeUiState.Success -> { LazyColumn( modifier = Modifier .fillMaxSize() .padding(paddingValues), contentPadding = PaddingValues(16.dp), verticalArrangement = Arrangement.spacedBy(8.dp) ) { items(state.items, key = { it.id }) { item -> ItemCard( item = item, onClick = { onItemClick(item.id) } ) } } } is HomeUiState.Error -> { ErrorContent( message = state.message, onRetry = viewModel::retry, modifier = Modifier.padding(paddingValues) ) } } } } @Composable private fun ItemCard( item: Item, onClick: () -> Unit, modifier: Modifier = Modifier ) { Card( onClick = onClick, modifier = modifier.fillMaxWidth() ) { Column( modifier = Modifier.padding(16.dp) ) { Text( text = item.title, style = MaterialTheme.typography.titleMedium ) Spacer(modifier = Modifier.height(4.dp)) Text( text = item.description, style = MaterialTheme.typography.bodyMedium, color = MaterialTheme.colorScheme.onSurfaceVariant ) } } }
kotlin// ui/feature/home/HomeViewModel.kt package com.example.myapp.ui.feature.home import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.* import kotlinx.coroutines.launch import javax.inject.Inject @HiltViewModel class HomeViewModel @Inject constructor( private val getItemsUseCase: GetItemsUseCase ) : ViewModel() { private val _uiState = MutableStateFlow<HomeUiState>(HomeUiState.Loading) val uiState: StateFlow<HomeUiState> = _uiState.asStateFlow() init { loadItems() } private fun loadItems() { viewModelScope.launch { _uiState.value = HomeUiState.Loading getItemsUseCase() .catch { e -> _uiState.value = HomeUiState.Error(e.message ?: "Unknown error") } .collect { items -> _uiState.value = HomeUiState.Success(items) } } } fun retry() { loadItems() } } sealed interface HomeUiState { data object Loading : HomeUiState data class Success(val items: List<Item>) : HomeUiState data class Error(val message: String) : HomeUiState }
kotlin// ui/navigation/NavGraph.kt package com.example.myapp.ui.navigation import androidx.compose.runtime.Composable import androidx.navigation.NavHostController import androidx.navigation.NavType import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable import androidx.navigation.navArgument import com.example.myapp.ui.feature.home.HomeScreen import com.example.myapp.ui.feature.detail.DetailScreen sealed class Screen(val route: String) { data object Home : Screen("home") data object Detail : Screen("detail/{itemId}") { fun createRoute(itemId: String) = "detail/$itemId" } } @Composable fun NavGraph( navController: NavHostController, startDestination: String = Screen.Home.route ) { NavHost( navController = navController, startDestination = startDestination ) { composable(Screen.Home.route) { HomeScreen( onItemClick = { itemId -> navController.navigate(Screen.Detail.createRoute(itemId)) } ) } composable( route = Screen.Detail.route, arguments = listOf( navArgument("itemId") { type = NavType.StringType } ) ) { backStackEntry -> val itemId = backStackEntry.arguments?.getString("itemId") ?: return@composable DetailScreen( itemId = itemId, onBackClick = { navController.popBackStack() } ) } } }
kotlin// di/AppModule.kt package com.example.myapp.di import android.content.Context import androidx.room.Room import com.example.myapp.data.local.AppDatabase import com.example.myapp.data.remote.ApiService import com.example.myapp.data.repository.ItemRepositoryImpl import com.example.myapp.domain.repository.ItemRepository import dagger.Binds import dagger.Module import dagger.Provides import dagger.hilt.InstallIn import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.components.SingletonComponent import retrofit2.Retrofit import retrofit2.converter.moshi.MoshiConverterFactory import javax.inject.Singleton @Module @InstallIn(SingletonComponent::class) abstract class RepositoryModule { @Binds @Singleton abstract fun bindItemRepository(impl: ItemRepositoryImpl): ItemRepository } @Module @InstallIn(SingletonComponent::class) object DatabaseModule { @Provides @Singleton fun provideDatabase(@ApplicationContext context: Context): AppDatabase { return Room.databaseBuilder( context, AppDatabase::class.java, "app_database" ).build() } @Provides fun provideItemDao(database: AppDatabase) = database.itemDao() } @Module @InstallIn(SingletonComponent::class) object NetworkModule { @Provides @Singleton fun provideRetrofit(): Retrofit { return Retrofit.Builder() .baseUrl("https://api.example.com/") .addConverterFactory(MoshiConverterFactory.create()) .build() } @Provides @Singleton fun provideApiService(retrofit: Retrofit): ApiService { return retrofit.create(ApiService::class.java) } }
bash# Clean build ./gradlew clean # Build debug APK ./gradlew assembleDebug # Build release AAB ./gradlew bundleRelease # Run unit tests ./gradlew testDebugUnitTest # Run instrumented tests ./gradlew connectedDebugAndroidTest # Run lint ./gradlew lintDebug # Install on device ./gradlew installDebug
bash ./gradlew --refresh-dependencies
bash ./gradlew clean && ./gradlew kspDebugKotlin
bash adb kill-server && adb start-server
bash # Invalidate caches: File > Invalidate Caches / Restart
android-room - Room database integrationfirebase-mobile - Firebase servicesmobile-testing - Comprehensive testinggoogle-play-console - Play Store publishing| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | pass→pass | 12,127 | 11,812 | -3% | 1 | 1 | 0% | 1,514 | 5,027 | +232% | 0 | 0 | — |
case-02 | pass→pass | 15,393 | 10,345 | -33% | 1 | 1 | 0% | 2,102 | 5,863 | +179% | 0 | 0 | — |
case-03 | pass→pass | 10,207 | 10,609 | +4% | 1 | 1 | 0% | 1,705 | 5,508 | +223% | 0 | 0 | — |
case-04 | pass→pass | 9,559 | 9,719 | +2% | 1 | 1 | 0% | 1,845 | 5,638 | +206% | 0 | 0 | — |
case-05 | pass→pass | 11,362 | 11,165 | -2% | 1 | 1 | 0% | 1,231 | 4,908 | +299% | 0 | 0 | — |
case-06 | pass→pass | 6,788 | 7,382 | +9% | 1 | 1 | 0% | 1,068 | 5,035 | +371% | 0 | 0 | — |
case-07 | pass→pass | 12,106 | 13,035 | +8% | 1 | 1 | 0% | 2,223 | 6,241 | +181% | 0 | 0 | — |
case-08 | fail→pass | 14,293 | 10,102 | -29% | 1 | 1 | 0% | 1,767 | 5,642 | +219% | 0 | 0 | — |
case-09 | pass→pass | 10,401 | 12,258 | +18% | 1 | 1 | 0% | 1,863 | 6,246 | +235% | 0 | 0 | — |
case-10 | pass→pass | 9,950 | 4,197 | -58% | 1 | 1 | 0% | 919 | 4,454 | +385% | 0 | 0 | — |
case-11 | pass→pass | 5,315 | 8,009 | +51% | 1 | 1 | 0% | 918 | 4,207 | +358% | 0 | 0 | — |
case-12 | pass→pass | 13,324 | 8,489 | -36% | 1 | 1 | 0% | 1,231 | 4,298 | +249% | 0 | 0 | — |
case-13 | pass→pass | 7,510 | 11,626 | +55% | 1 | 1 | 0% | 1,376 | 4,861 | +253% | 0 | 0 | — |
case-14 | pass→pass | 7,801 | 12,417 | +59% | 1 | 1 | 0% | 1,593 | 5,305 | +233% | 0 | 0 | — |
case-15 | pass→pass | 4,638 | 9,567 | +106% | 1 | 1 | 0% | 886 | 4,668 | +427% | 0 | 0 | — |
case-16 | fail→pass | 17,408 | 6,972 | -60% | 1 | 1 | 0% | 2,072 | 4,005 | +93% | 0 | 0 | — |
case-17 | pass→pass | 7,129 | 7,055 | -1% | 1 | 1 | 0% | 954 | 3,927 | +312% | 0 | 0 | — |
case-18 | pass→pass | 12,325 | 10,306 | -16% | 1 | 1 | 0% | 1,298 | 4,596 | +254% | 0 | 0 | — |
case-19 | pass→pass | 13,575 | 15,599 | +15% | 1 | 1 | 0% | 1,601 | 5,560 | +247% | 0 | 0 | — |
case-20 | pass→pass | 11,887 | 10,133 | -15% | 1 | 1 | 0% | 1,308 | 4,695 | +259% | 0 | 0 | — |
case-21 | pass→pass | 7,980 | 13,288 | +67% | 1 | 1 | 0% | 1,663 | 5,311 | +219% | 0 | 0 | — |
case-22 | pass→pass | 13,697 | 16,841 | +23% | 1 | 1 | 0% | 2,326 | 5,859 | +152% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +9 percentage points is the difference between those two pass rates over the 22 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.