home / skills / personamanagmentlayer / pcl / kotlin-expert

kotlin-expert skill

/stdlib/languages/kotlin-expert

This skill helps you master Kotlin development for Android and multiplatform projects, improving productivity with coroutines, Jetpack Compose, and clean

npx playbooks add skill personamanagmentlayer/pcl --skill kotlin-expert

Review the files below or copy the command above to add this skill to your agents.

Files (1)
SKILL.md
9.1 KB
---
name: kotlin-expert
version: 1.0.0
description: Expert-level Kotlin development, Android, coroutines, and multiplatform
category: languages
tags: [kotlin, android, coroutines, multiplatform, jvm]
allowed-tools:
  - Read
  - Write
  - Edit
  - Bash(kotlin:*, gradle:*)
---

# Kotlin Expert

Expert guidance for Kotlin development, Android, coroutines, Kotlin Multiplatform, and modern JVM development.

## Core Concepts

### Kotlin Fundamentals
- Null safety
- Extension functions
- Data classes
- Sealed classes
- Coroutines and Flow
- Higher-order functions

### Android Development
- Jetpack Compose
- ViewModel and LiveData
- Room database
- Retrofit networking
- Dependency injection (Hilt)
- Android lifecycle

### Kotlin Multiplatform
- Shared business logic
- Platform-specific implementations
- iOS and Android targets
- Common module architecture

## Modern Kotlin Syntax

```kotlin
// Data classes
data class User(
    val id: String,
    val name: String,
    val email: String,
    val createdAt: LocalDateTime = LocalDateTime.now()
)

// Sealed classes for type-safe states
sealed class Result<out T> {
    data class Success<T>(val data: T) : Result<T>()
    data class Error(val exception: Exception) : Result<Nothing>()
    object Loading : Result<Nothing>()
}

// Extension functions
fun String.isValidEmail(): Boolean {
    return this.contains("@") && this.contains(".")
}

// Scope functions
fun processUser(user: User) {
    user.run {
        println("Processing user: $name")
        // 'this' refers to user
    }

    user.let { u ->
        // 'it' or custom name refers to user
        println(u.email)
    }

    user.apply {
        // Modify properties
        // Returns the object
    }
}

// Null safety
fun findUser(id: String): User? {
    return database.find(id)
}

val user = findUser("123")
val name = user?.name ?: "Unknown" // Elvis operator
user?.let { println(it.name) } // Safe call with let

// When expression
fun getUserStatus(user: User): String = when {
    user.isActive && user.isPremium -> "Premium Active"
    user.isActive -> "Active"
    else -> "Inactive"
}
```

## Coroutines and Flow

```kotlin
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*

class UserRepository {
    private val api: UserApi

    // Suspend function
    suspend fun fetchUser(id: String): User {
        return withContext(Dispatchers.IO) {
            api.getUser(id)
        }
    }

    // Flow for reactive streams
    fun observeUsers(): Flow<List<User>> = flow {
        while (true) {
            val users = fetchUsers()
            emit(users)
            delay(5000) // Refresh every 5 seconds
        }
    }.flowOn(Dispatchers.IO)

    // StateFlow for state management
    private val _users = MutableStateFlow<List<User>>(emptyList())
    val users: StateFlow<List<User>> = _users.asStateFlow()

    suspend fun refreshUsers() {
        _users.value = fetchUsers()
    }
}

// Coroutine scopes
class UserViewModel : ViewModel() {
    private val repository = UserRepository()

    fun loadUsers() {
        viewModelScope.launch {
            try {
                val users = repository.fetchUser("123")
                // Update UI
            } catch (e: Exception) {
                // Handle error
            }
        }
    }

    // Parallel execution
    suspend fun loadMultipleUsers(ids: List<String>): List<User> {
        return coroutineScope {
            ids.map { id ->
                async { repository.fetchUser(id) }
            }.awaitAll()
        }
    }

    // Flow transformation
    fun searchUsers(query: String): Flow<List<User>> {
        return repository.observeUsers()
            .map { users -> users.filter { it.name.contains(query, ignoreCase = true) } }
            .distinctUntilChanged()
            .debounce(300)
    }
}
```

## Android Jetpack Compose

```kotlin
import androidx.compose.foundation.layout.*
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier

@Composable
fun UserListScreen(viewModel: UserViewModel = viewModel()) {
    val users by viewModel.users.collectAsState()
    val isLoading by viewModel.isLoading.collectAsState()

    Scaffold(
        topBar = { TopAppBar(title = { Text("Users") }) }
    ) { padding ->
        if (isLoading) {
            CircularProgressIndicator(
                modifier = Modifier.fillMaxSize()
            )
        } else {
            LazyColumn(
                modifier = Modifier.padding(padding)
            ) {
                items(users) { user ->
                    UserCard(user = user)
                }
            }
        }
    }
}

@Composable
fun UserCard(user: User) {
    Card(
        modifier = Modifier
            .fillMaxWidth()
            .padding(8.dp)
    ) {
        Column(modifier = Modifier.padding(16.dp)) {
            Text(
                text = user.name,
                style = MaterialTheme.typography.headlineSmall
            )
            Text(
                text = user.email,
                style = MaterialTheme.typography.bodyMedium
            )
        }
    }
}
```

## Room Database

```kotlin
import androidx.room.*
import kotlinx.coroutines.flow.Flow

@Entity(tableName = "users")
data class UserEntity(
    @PrimaryKey val id: String,
    @ColumnInfo(name = "name") val name: String,
    @ColumnInfo(name = "email") val email: String,
    @ColumnInfo(name = "created_at") val createdAt: Long
)

@Dao
interface UserDao {
    @Query("SELECT * FROM users")
    fun getAllUsers(): Flow<List<UserEntity>>

    @Query("SELECT * FROM users WHERE id = :userId")
    suspend fun getUserById(userId: String): UserEntity?

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    suspend fun insertUser(user: UserEntity)

    @Update
    suspend fun updateUser(user: UserEntity)

    @Delete
    suspend fun deleteUser(user: UserEntity)

    @Query("DELETE FROM users WHERE id = :userId")
    suspend fun deleteUserById(userId: String)
}

@Database(entities = [UserEntity::class], version = 1)
abstract class AppDatabase : RoomDatabase() {
    abstract fun userDao(): UserDao

    companion object {
        @Volatile
        private var INSTANCE: AppDatabase? = null

        fun getDatabase(context: Context): AppDatabase {
            return INSTANCE ?: synchronized(this) {
                val instance = Room.databaseBuilder(
                    context.applicationContext,
                    AppDatabase::class.java,
                    "app_database"
                ).build()
                INSTANCE = instance
                instance
            }
        }
    }
}
```

## Dependency Injection with Hilt

```kotlin
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton

@Module
@InstallIn(SingletonComponent::class)
object NetworkModule {

    @Provides
    @Singleton
    fun provideRetrofit(): Retrofit {
        return Retrofit.Builder()
            .baseUrl("https://api.example.com")
            .addConverterFactory(GsonConverterFactory.create())
            .build()
    }

    @Provides
    @Singleton
    fun provideUserApi(retrofit: Retrofit): UserApi {
        return retrofit.create(UserApi::class.java)
    }
}

@HiltAndroidApp
class MyApplication : Application()

@AndroidEntryPoint
class MainActivity : ComponentActivity() {
    @Inject lateinit var repository: UserRepository
}
```

## Kotlin Multiplatform

```kotlin
// commonMain
expect class Platform() {
    val name: String
}

expect fun platformSpecificFunction(): String

// androidMain
actual class Platform actual constructor() {
    actual val name: String = "Android ${android.os.Build.VERSION.SDK_INT}"
}

actual fun platformSpecificFunction(): String = "Android implementation"

// iosMain
actual class Platform actual constructor() {
    actual val name: String = UIDevice.currentDevice.systemName()
}

actual fun platformSpecificFunction(): String = "iOS implementation"

// Shared business logic
class UserService {
    suspend fun fetchUser(id: String): User {
        // Shared logic works on all platforms
        return api.getUser(id)
    }
}
```

## Best Practices

### Kotlin Style
- Use val over var when possible
- Leverage null safety features
- Use data classes for DTOs
- Prefer extension functions
- Use sealed classes for type-safe states
- Follow naming conventions

### Coroutines
- Use appropriate dispatchers (IO, Main, Default)
- Handle cancellation properly
- Avoid GlobalScope
- Use structured concurrency
- Prefer Flow over LiveData
- Use StateFlow for state

### Android
- Follow MVVM architecture
- Use Jetpack Compose for UI
- Implement proper lifecycle handling
- Use dependency injection
- Handle configuration changes
- Implement proper error handling

## Anti-Patterns

❌ Using !! (non-null assertion)
❌ GlobalScope.launch
❌ Blocking main thread
❌ Not handling coroutine cancellation
❌ Tight coupling
❌ God classes
❌ Ignoring memory leaks

## Resources

- Kotlin Docs: https://kotlinlang.org/docs/
- Android Developers: https://developer.android.com/kotlin
- Kotlin Coroutines: https://kotlinlang.org/docs/coroutines-overview.html
- Jetpack Compose: https://developer.android.com/jetpack/compose

Overview

This skill provides expert-level guidance for Kotlin development, covering Android, coroutines, and Kotlin Multiplatform. It focuses on practical patterns, modern syntax, and architecture for reliable JVM and multiplatform applications. Use it to improve code quality, performance, and cross-platform sharing of business logic.

How this skill works

The skill inspects code patterns, recommends idiomatic Kotlin constructs (null safety, extension and data classes, sealed types), and advises on coroutine usage and Flow/StateFlow for reactive streams. It analyzes Android-specific areas such as Jetpack Compose, lifecycle-aware components, Room, Retrofit, and Hilt integration. For multiplatform projects it outlines common module structure and platform-specific expect/actual implementations.

When to use it

  • When migrating Java codebases to idiomatic Kotlin
  • When designing coroutine-based concurrency and reactive flows
  • When building modern Android UIs with Jetpack Compose
  • When architecting Kotlin Multiplatform shared logic
  • When setting up persistence, networking, and DI (Room, Retrofit, Hilt)
  • When optimizing performance and lifecycle safety in production apps

Best practices

  • Prefer val over var and leverage Kotlin null-safety operators
  • Use sealed classes for UI/network states and data classes for DTOs
  • Use structured concurrency, appropriate Dispatchers, and avoid GlobalScope
  • Prefer Flow/StateFlow for reactive streams and use debounce/distinctUntilChanged
  • Follow MVVM on Android, keep UI stateless, and inject dependencies with Hilt
  • Design commonMain for shared business logic and keep platform code minimal

Example use cases

  • Review coroutine usage to prevent cancellation leaks and offload IO to Dispatchers.IO
  • Convert legacy View-based screens to Jetpack Compose components with ViewModel integration
  • Design a shared network and domain layer for Android and iOS using Kotlin Multiplatform
  • Implement Room database with Flow-backed DAOs and migration-safe entities
  • Set up Retrofit + Hilt network module with testable repository patterns

FAQ

Should I use Flow or LiveData for new Android apps?

Prefer Flow/StateFlow for new code due to better composability and coroutine support; interop with LiveData is possible if needed.

How do I handle platform-specific APIs in multiplatform projects?

Keep shared business logic in commonMain and declare expect/actual for platform-specific implementations to isolate platform dependencies.