android-kotlin
This tool assists with Android development using Hilt for dependency injection, Coroutines for asynchronous tasks, and Compose for UI building.
Install
mkdir -p .claude/skills/android-kotlin && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/2109" && unzip -o skill.zip -d .claude/skills/android-kotlin && rm skill.zipInstalls to .claude/skills/android-kotlin
Activation
This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.
Android Kotlin development with Coroutines, Jetpack Compose, Hilt, and MockK testingKey capabilities
- →Implement UI features with Jetpack Compose
- →Configure dependency injection with Hilt
- →Manage asynchronous tasks with Coroutines and Flow
- →Write unit tests using MockK and Turbine
- →Define Gradle configurations for Android projects
How it works
It provides templates and best practices for Android development, including ViewModel state management, repository patterns, and testing strategies.
Inputs & outputs
When to use android-kotlin
- →Implement new UI features using Jetpack Compose
- →Set up Hilt dependency injection modules
- →Write unit tests with MockK
About this skill
Android Kotlin Skill
Project Structure
project/
├── app/
│ ├── src/
│ │ ├── main/
│ │ │ ├── kotlin/com/example/app/
│ │ │ │ ├── data/ # Data layer
│ │ │ │ │ ├── local/ # Room database
│ │ │ │ │ ├── remote/ # Retrofit/Ktor services
│ │ │ │ │ └── repository/ # Repository implementations
│ │ │ │ ├── di/ # Hilt modules
│ │ │ │ ├── domain/ # Business logic
│ │ │ │ │ ├── model/ # Domain models
│ │ │ │ │ ├── repository/ # Repository interfaces
│ │ │ │ │ └── usecase/ # Use cases
│ │ │ │ ├── ui/ # Presentation layer
│ │ │ │ │ ├── feature/ # Feature screens
│ │ │ │ │ │ ├── FeatureScreen.kt # Compose UI
│ │ │ │ │ │ └── FeatureViewModel.kt
│ │ │ │ │ ├── components/ # Reusable Compose components
│ │ │ │ │ └── theme/ # Material theme
│ │ │ │ └── App.kt # Application class
│ │ │ ├── res/
│ │ │ └── AndroidManifest.xml
│ │ ├── test/ # Unit tests
│ │ └── androidTest/ # Instrumentation tests
│ └── build.gradle.kts
├── build.gradle.kts # Project-level build file
├── gradle.properties
├── settings.gradle.kts
└── CLAUDE.md
Gradle Configuration (Kotlin DSL)
App-level build.gradle.kts
plugins {
id("com.android.application")
id("org.jetbrains.kotlin.android")
id("com.google.dagger.hilt.android")
id("com.google.devtools.ksp")
}
android {
namespace = "com.example.app"
compileSdk = 34
defaultConfig {
applicationId = "com.example.app"
minSdk = 24
targetSdk = 34
versionCode = 1
versionName = "1.0"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
isMinifyEnabled = true
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
kotlinOptions {
jvmTarget = "17"
}
buildFeatures {
compose = true
}
composeOptions {
kotlinCompilerExtensionVersion = "1.5.8"
}
}
dependencies {
// Compose BOM
val composeBom = platform("androidx.compose:compose-bom:2024.01.00")
implementation(composeBom)
implementation("androidx.compose.ui:ui")
implementation("androidx.compose.ui:ui-tooling-preview")
implementation("androidx.compose.material3:material3")
implementation("androidx.activity:activity-compose:1.8.2")
implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.7.0")
// Coroutines
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3")
// Hilt
implementation("com.google.dagger:hilt-android:2.50")
ksp("com.google.dagger:hilt-compiler:2.50")
implementation("androidx.hilt:hilt-navigation-compose:1.1.0")
// Room
implementation("androidx.room:room-runtime:2.6.1")
implementation("androidx.room:room-ktx:2.6.1")
ksp("androidx.room:room-compiler:2.6.1")
// Testing
testImplementation("junit:junit:4.13.2")
testImplementation("io.mockk:mockk:1.13.9")
testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.7.3")
testImplementation("app.cash.turbine:turbine:1.0.0")
androidTestImplementation("androidx.test.ext:junit:1.1.5")
androidTestImplementation("androidx.compose.ui:ui-test-junit4")
debugImplementation("androidx.compose.ui:ui-tooling")
debugImplementation("androidx.compose.ui:ui-test-manifest")
}
Kotlin Coroutines & Flow
ViewModel with StateFlow
@HiltViewModel
class UserViewModel @Inject constructor(
private val getUserUseCase: GetUserUseCase,
private val savedStateHandle: SavedStateHandle
) : ViewModel() {
private val _uiState = MutableStateFlow(UserUiState())
val uiState: StateFlow<UserUiState> = _uiState.asStateFlow()
private val userId: String = checkNotNull(savedStateHandle["userId"])
init {
loadUser()
}
fun loadUser() {
viewModelScope.launch {
_uiState.update { it.copy(isLoading = true) }
getUserUseCase(userId)
.catch { e ->
_uiState.update {
it.copy(isLoading = false, error = e.message)
}
}
.collect { user ->
_uiState.update {
it.copy(isLoading = false, user = user, error = null)
}
}
}
}
fun clearError() {
_uiState.update { it.copy(error = null) }
}
}
data class UserUiState(
val user: User? = null,
val isLoading: Boolean = false,
val error: String? = null
)
Repository with Flow
interface UserRepository {
fun getUser(userId: String): Flow<User>
fun observeUsers(): Flow<List<User>>
suspend fun saveUser(user: User)
}
class UserRepositoryImpl @Inject constructor(
private val api: UserApi,
private val dao: UserDao,
private val dispatcher: CoroutineDispatcher = Dispatchers.IO
) : UserRepository {
override fun getUser(userId: String): Flow<User> = flow {
// Emit cached data first
dao.getUserById(userId)?.let { emit(it) }
// Fetch from network and update cache
val remoteUser = api.getUser(userId)
dao.insert(remoteUser)
emit(remoteUser)
}.flowOn(dispatcher)
override fun observeUsers(): Flow<List<User>> =
dao.observeAllUsers().flowOn(dispatcher)
override suspend fun saveUser(user: User) = withContext(dispatcher) {
api.saveUser(user)
dao.insert(user)
}
}
Jetpack Compose
Screen with ViewModel
@Composable
fun UserScreen(
viewModel: UserViewModel = hiltViewModel(),
onNavigateBack: () -> Unit
) {
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
UserScreenContent(
uiState = uiState,
onRefresh = viewModel::loadUser,
onErrorDismiss = viewModel::clearError,
onNavigateBack = onNavigateBack
)
}
@Composable
private fun UserScreenContent(
uiState: UserUiState,
onRefresh: () -> Unit,
onErrorDismiss: () -> Unit,
onNavigateBack: () -> Unit
) {
Scaffold(
topBar = {
TopAppBar(
title = { Text("User Profile") },
navigationIcon = {
IconButton(onClick = onNavigateBack) {
Icon(Icons.AutoMirrored.Filled.ArrowBack, "Back")
}
}
)
}
) { padding ->
Box(
modifier = Modifier
.fillMaxSize()
.padding(padding)
) {
when {
uiState.isLoading -> {
CircularProgressIndicator(
modifier = Modifier.align(Alignment.Center)
)
}
uiState.user != null -> {
UserContent(user = uiState.user)
}
}
uiState.error?.let { error ->
Snackbar(
modifier = Modifier.align(Alignment.BottomCenter),
action = {
TextButton(onClick = onErrorDismiss) {
Text("Dismiss")
}
}
) {
Text(error)
}
}
}
}
}
Sealed Classes for State
Result Wrapper
sealed interface Result<out T> {
data class Success<T>(val data: T) : Result<T>
data class Error(val exception: Throwable) : Result<Nothing>
data object Loading : Result<Nothing>
}
fun <T> Result<T>.getOrNull(): T? = (this as? Result.Success)?.data
inline fun <T, R> Result<T>.map(transform: (T) -> R): Result<R> = when (this) {
is Result.Success -> Result.Success(transform(data))
is Result.Error -> this
is Result.Loading -> this
}
Testing with MockK & Turbine
ViewModel Tests
@OptIn(ExperimentalCoroutinesApi::class)
class UserViewModelTest {
@get:Rule
val mainDispatcherRule = MainDispatcherRule()
private val getUserUseCase: GetUserUseCase = mockk()
private val savedStateHandle = SavedStateHandle(mapOf("userId" to "123"))
private lateinit var viewModel: UserViewModel
@Before
fun setup() {
viewModel = UserViewModel(getUserUseCase, savedStateHandle)
}
@Test
fun `loadUser success updates state with user`() = runTest {
val user = User("123", "John Doe", "[email protected]")
coEvery { getUserUseCase("123") } returns flowOf(user)
viewModel.uiState.test {
val initial = awaitItem()
assertFalse(initial.isLoading)
viewModel.loadUser()
val loading = awaitItem()
assertTrue(loading.isLoading)
val success = awaitItem()
assertFalse(success.isLoading)
assertEquals(user, success.user)
}
}
}
class MainDispatcherRule(
private val dispatcher: TestDispatcher = UnconfinedTestDispatcher()
) : TestWatcher() {
override fun starting(description: Description) {
Dispatchers.setMain(dispatcher)
}
override fun finished(description: Description) {
Dispatchers.resetMain()
}
}
GitHub Actions
name: Android Kotlin CI
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
build:
runs-on: ubun
---
*Content truncated.*
When not to use it
- →Non-Android projects
- →Projects not using Kotlin
Prerequisites
Limitations
- →Requires Kotlin DSL for Gradle
- →Limited to modern Android stack
How it compares
It enforces modern Android architecture patterns like Hilt and Compose rather than legacy XML or View-based approaches.
Compared to similar skills
android-kotlin side by side with the closest alternatives in the catalog.
| Skill | Installs | Updated | Safety | Difficulty |
|---|---|---|---|---|
| android-kotlin (this skill) | 7 | 4mo | No flags | Intermediate |
| android-jetpack-compose-expert | 4 | 4mo | No flags | Advanced |
| testing-android-code | 6 | 3mo | No flags | Intermediate |
| reviewing-changes | 1 | 3mo | No flags | Intermediate |
Try saying
Example prompts that trigger this skill in your AI assistant.
More by alinaqi
View all by alinaqi →You might also like
android-jetpack-compose-expert
sickn33
Expert guidance for building modern Android UIs with Jetpack Compose, covering state management, navigation, performance, and Material Design 3.
testing-android-code
bitwarden
This skill should be used when writing or reviewing tests for Android code in Bitwarden. Triggered by "BaseViewModelTest", "BitwardenComposeTest", "BaseServiceTest", "stateEventFlow", "bufferedMutableSharedFlow", "FakeDispatcherManager", "expectNoEvents", "assertCoroutineThrows", "createMockCipher", "createMockSend", "asSuccess", "Why is my Bitwarden test failing?", or testing questions about ViewModels, repositories, Compose screens, or data sources in Bitwarden.
reviewing-changes
bitwarden
Guides Android code reviews with type-specific checklists and MVVM/Compose pattern validation. Use when reviewing Android PRs, pull requests, diffs, or local changes involving Kotlin, ViewModel, Composable, Repository, or Gradle files. Triggered by "review PR", "review changes", "check this code", "Android review", or code review requests mentioning bitwarden/android. Loads specialized checklists for feature additions, bug fixes, UI refinements, refactoring, dependency updates, and infrastructure changes.
trail-sense-android-tests
kylecorry31
Add UI automation tests to Trail-Sense Android app using AutomationLibrary. Use when asked to create, add, write, or implement automated tests, UI tests, integration tests, or androidTests for Trail Sense tools. Covers test class structure, AutomationLibrary functions, and testing patterns.
kmp-cmp-app-builder
firdaus1453
>
android-emulator-skill
new-silvermoon
Production-ready scripts for Android app testing, building, and automation. Provides semantic UI navigation, build automation, log monitoring, and emulator lifecycle management. Optimized for AI agents with minimal token output.