Communitygithub.com

ImL1s/flutter-claude-skills

66 kuratierte Claude Code-Fähigkeiten für Flutter / Dart-Entwicklung, Tests, Releases und KI-gestützte Design-to-Code-Pipelines.

Was ist flutter-claude-skills?

Dieses Skill-Set bietet Entwicklern eine umfassende Sammlung von 66 speziell kuratierten Fähigkeiten für Claude Code, die auf die Entwicklung mit Flutter und Dart zugeschnitten sind. Es deckt eine breite Palette von Aufgaben ab, darunter Codierung, Testautomatisierung, Release-Management und KI-unterstützte „Design-zu-Code“-Pipelines. Das Skill-Set ist darauf ausgelegt, den Entwicklungsprozess zu optimieren, indem es sofort einsatzbereite Anleitungen und Muster für häufige Flutter-/Dart-Aufgaben bietet. Es unterstützt Entwickler dabei, die Leistungsfähigkeit von Claude Code zu nutzen, um die Produktivität zu steigern und die Code-Qualität während des gesamten Entwicklungslebenszyklus zu verbessern. Die Skills sind sorgfältig organisiert und abgestimmt, um die Effizienz von KI-Assistenten bei Flutter-Projekten zu maximieren.

Funktioniert mit✓Claude Code~Codex CLI~Cursor
npx skills add ImL1s/flutter-claude-skills

In Ihrer bevorzugten KI fragen

Öffnet einen neuen Chat, in dem dieser Agent-Skill bereits geladen ist.

Dokumentation

Kotlin Multiplatform (KMP) Grundlagen

Kotlin Multiplatform ermöglicht die gemeinsame Nutzung von Code auf Android, iOS, Desktop, Web (WASM) und Server.

Projektstruktur

Multi-Modul-Architektur (Feature-basiert + api/impl)

your-project-admin/
├── build.gradle.kts              # Root-Build-Konfiguration
├── settings.gradle.kts           # Moduleinschlüsse
├── gradle/libs.versions.toml     # Versionskatalog
│
├── core/
│   ├── common/                   # Utilities, Ergebnis-Typen, Erweiterungen
│   │   └── src/commonMain/kotlin/
│   ├── data/                     # Datenabstraktionen, DataStore
│   │   ├── src/commonMain/kotlin/
│   │   ├── src/androidMain/kotlin/
│   │   └── src/iosMain/kotlin/
│   ├── database/                 # Room (nur Android/iOS/JVM)
│   │   └── src/commonMain/kotlin/
│   ├── network/                  # Ktor-Client
│   │   └── src/commonMain/kotlin/
│   └── ui/                       # Designsystem, Theme
│       └── src/commonMain/kotlin/
│
├── feature/
│   ├── auth/
│   │   ├── api/                  # Öffentliche Schnittstellen, Modelle
│   │   │   └── src/commonMain/kotlin/
│   │   └── impl/                 # Implementierung, UI
│   │       └── src/commonMain/kotlin/
│   └── home/
│       ├── api/
│       └── impl/
│
├── composeApp/                   # Plattform-Einstiegspunkte
│   ├── src/commonMain/           # App-Komposition, DI-Graph
│   ├── src/androidMain/          # MainActivity
│   ├── src/iosMain/              # iOS-Einstieg
│   ├── src/jvmMain/              # Desktop main()
│   └── src/wasmJsMain/           # Web-Einstieg
│
└── iosApp/                       # Xcode-Projekt

Quellsatz-Hierarchie

commonMain
├── androidMain
├── iosMain
│   ├── iosX64Main
│   ├── iosArm64Main
│   └── iosSimulatorArm64Main
├── jvmMain
├── wasmJsMain
└── jsMain (Fallback)

Gradle-Einrichtung

build.gradle.kts (Root)

plugins {
    alias(libs.plugins.kotlinMultiplatform) apply false
    alias(libs.plugins.androidApplication) apply false
    alias(libs.plugins.androidLibrary) apply false
    alias(libs.plugins.composeMultiplatform) apply false
    alias(libs.plugins.composeCompiler) apply false
    alias(libs.plugins.kotlinSerialization) apply false
    alias(libs.plugins.ksp) apply false
    alias(libs.plugins.room) apply false
    alias(libs.plugins.metro) apply false
}

settings.gradle.kts

pluginManagement {
    repositories {
        google()
        mavenCentral()
        gradlePluginPortal()
    }
}

dependencyResolutionManagement {
    repositories {
        google()
        mavenCentral()
    }
}

rootProject.name = "your-project-admin"

// Kernmodule
include(":core:common")
include(":core:data")
include(":core:database")
include(":core:network")
include(":core:ui")

// Feature-Module
include(":feature:auth:api")
include(":feature:auth:impl")
include(":feature:home:api")
include(":feature:home:impl")

// App-Einstiegspunkte
include(":composeApp")

gradle/libs.versions.toml

[versions]
kotlin = "2.1.0"
agp = "8.7.3"
compose-multiplatform = "1.7.3"
ktor = "3.1.1"
room = "2.8.4"
datastore = "1.2.0"
decompose = "3.5.0"
metro = "0.1.1"
essenty = "2.5.0"
coroutines = "1.10.1"
serialization = "1.7.3"
ksp = "2.1.0-1.0.29"

[libraries]
# Kotlin
kotlinx-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "coroutines" }
kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "serialization" }

# Ktor
ktor-client-core = { module = "io.ktor:ktor-client-core", version.ref = "ktor" }
ktor-client-cio = { module = "io.ktor:ktor-client-cio", version.ref = "ktor" }
ktor-client-darwin = { module = "io.ktor:ktor-client-darwin", version.ref = "ktor" }
ktor-client-content-negotiation = { module = "io.ktor:ktor-client-content-negotiation", version.ref = "ktor" }
ktor-serialization-kotlinx-json = { module = "io.ktor:ktor-serialization-kotlinx-json", version.ref = "ktor" }

# Room (nur Android, iOS, JVM)
androidx-room-runtime = { module = "androidx.room:room-runtime", version.ref = "room" }
androidx-room-compiler = { module = "androidx.room:room-compiler", version.ref = "room" }
androidx-sqlite-bundled = { module = "androidx.sqlite:sqlite-bundled", version = "2.6.2" }

# DataStore
datastore-preferences-core = { module = "androidx.datastore:datastore-preferences-core", version.ref = "datastore" }

# Decompose
decompose = { module = "com.arkivanov.decompose:decompose", version.ref = "decompose" }
decompose-compose = { module = "com.arkivanov.decompose:extensions-compose", version.ref = "decompose" }
essenty-lifecycle = { module = "com.arkivanov.essenty:lifecycle", version.ref = "essenty" }

[plugins]
kotlinMultiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" }
kotlinSerialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }
androidApplication = { id = "com.android.application", version.ref = "agp" }
androidLibrary = { id = "com.android.library", version.ref = "agp" }
composeMultiplatform = { id = "org.jetbrains.compose", version.ref = "compose-multiplatform" }
composeCompiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" }
room = { id = "androidx.room", version.ref = "room" }
metro = { id = "dev.zacsweers.metro", version.ref = "metro" }

Modul build.gradle.kts (KMP-Bibliothek)

// core/common/build.gradle.kts
plugins {
    alias(libs.plugins.kotlinMultiplatform)
    alias(libs.plugins.androidLibrary)
}

kotlin {
    androidTarget {
        compilations.all {
            kotlinOptions {
                jvmTarget = "17"
            }
        }
    }

    listOf(
        iosX64(),
        iosArm64(),
        iosSimulatorArm64()
    ).forEach { iosTarget ->
        iosTarget.binaries.framework {
            baseName = "CoreCommon"
            isStatic = true
        }
    }

    jvm("desktop")

    @OptIn(ExperimentalWasmDsl::class)
    wasmJs {
        browser()
    }

    sourceSets {
        commonMain.dependencies {
            implementation(libs.kotlinx.coroutines.core)
        }

        androidMain.dependencies {
            // Android-spezifisch
        }

        iosMain.dependencies {
            // iOS-spezifisch
        }

        val desktopMain by getting {
            dependencies {
                // Desktop-spezifisch
            }
        }
    }
}

android {
    namespace = "com.your-project.admin.core.common"
    compileSdk = 35

    defaultConfig {
        minSdk = 24
    }

    compileOptions {
        sourceCompatibility = JavaVersion.VERSION_17
        targetCompatibility = JavaVersion.VERSION_17
    }
}

expect/actual-Muster

Deklaration (commonMain)

// commonMain/kotlin/Platform.kt
expect class PlatformContext

expect fun getPlatformName(): String

expect fun createDataStorePath(context: PlatformContext): String

Android-Implementierung

// androidMain/kotlin/Platform.android.kt
actual typealias PlatformContext = android.content.Context

actual fun getPlatformName(): String = "Android ${android.os.Build.VERSION.SDK_INT}"

actual fun createDataStorePath(context: PlatformContext): String {
    return context.filesDir.resolve("datastore").absolutePath
}

iOS-Implementierung

// iosMain/kotlin/Platform.ios.kt
import platform.Foundation.NSDocumentDirectory
import platform.Foundation.NSFileManager
import platform.Foundation.NSUserDomainMask

actual class PlatformContext

actual fun getPlatformName(): String = "iOS"

actual fun createDataStorePath(context: PlatformContext): String {
    val documentDir = NSFileManager.defaultManager.URLForDirectory(
        NSDocumentDirectory,
        NSUserDomainMask,
        null,
        false,
        null
    )
    return "${documentDir?.path}/datastore"
}

Desktop-Implementierung

// desktopMain/kotlin/Platform.jvm.kt
import java.io.File

actual class PlatformContext

actual fun getPlatformName(): String =
    "${System.getProperty("os.name")} ${System.getProperty("os.version")}"

actual fun createDataStorePath(context: PlatformContext): String {
    val home = System.getProperty("user.home")
    return File(home, ".your-project-admin/datastore").absolutePath
}

WASM-Implementierung

// wasmJsMain/kotlin/Platform.wasmJs.kt
actual class PlatformContext

actual fun getPlatformName(): String = "Web (WASM)"

actual fun createDataStorePath(context: PlatformContext): String {
    return "your-project-admin-datastore"  // Verwendet localStorage
}

Modulabhängigkeiten

Abhängigkeitsregeln

composeApp
├── feature:auth:impl
│   ├── feature:auth:api
│   ├── core:ui
│   └── core:network
├── feature:home:impl
│   ├── feature:home:api
│   ├── core:ui
│   └── core:database
├── core:ui
│   └── core:common
├── core:network
│   └── core:common
├── core:database
│   └── core:common
└── core:data
    └── core:common

api/impl-Muster

// feature/auth/api/build.gradle.kts
plugins {
    alias(libs.plugins.kotlinMultiplatform)
}

kotlin {
    // Ziele...

    sourceSets {
        commonMain.dependencies {
            // Nur Modelle und Schnittstellen – keine Implementierungen
            api(projects.core.common)
        }
    }
}

// feature/auth/impl/build.gradle.kts
plugins {
    alias(libs.plugins.kotlinMultiplatform)
    alias(libs.plugins.composeMultiplatform)
    alias(libs.plugins.composeCompiler)
}

kotlin {
    sourceSets {
        commonMain.dependencies {
            implementation(projects.feature.auth.api)
            implementation(projects.core.ui)
            implementation(projects.core.network)
            implementation(libs.decompose)
            implementation(libs.decompose.compose)
        }
    }
}

Allgemeine Muster

Ergebnis-Typ

// core/common/src/commonMain/kotlin/Result.kt
sealed class AppResult<out T> {
    data class Success<T>(val data: T) : AppResult<T>()
    data class Error(val message: String, val cause: Throwable? = null) : AppResult<Nothing>()
    data object Loading : AppResult<Nothing>()
}

inline fun <T, R> AppResult<T>.map(transform: (T) -> R): AppResult<R> = when (this) {
    is AppResult.Success -> AppResult.Success(transform(data))
    is AppResult.Error -> this
    is AppResult.Loading -> this
}

inline fun <T> AppResult<T>.onSuccess(action: (T) -> Unit): AppResult<T> {
    if (this is AppResult.Success) action(data)
    return this
}

inline fun <T> AppResult<T>.onError(action: (String, Throwable?) -> Unit): AppResult<T> {
    if (this is AppResult.Error) action(message, cause)
    return this
}

Repository-Schnittstelle (api-Modul)

// feature/auth/api/src/commonMain/kotlin/AuthRepository.kt
interface AuthRepository {
    suspend fun login(email: String, password: String): AppResult<User>
    suspend fun logout(): AppResult<Unit>
    fun observeAuthState(): Flow<AuthState>
}

data class User(
    val id: String,
    val email: String,
    val name: String
)

sealed class AuthState {
    data object Unauthenticated : AuthState()
    data class Authenticated(val user: User) : AuthState()
}

Repository-Implementierung (impl-Modul)

// feature/auth/impl/src/commonMain/kotlin/AuthRepositoryImpl.kt
class AuthRepositoryImpl(
    private val apiService: AuthApiService,
    private val tokenStorage: TokenStorage
) : AuthRepository {

    private val _authState = MutableStateFlow<AuthState>(AuthState.Unauthenticated)

    override suspend fun login(email: String, password: String): AppResult<User> {
        return try {
            val response = apiService.login(LoginRequest(email, password))
            tokenStorage.saveToken(response.token)
            _authState.value = AuthState.Authenticated(response.user)
            AppResult.Success(response.user)
        } catch (e: Exception) {
            AppResult.Error("Login fehlgeschlagen: ${e.message}", e)
        }
    }

    override suspend fun logout(): AppResult<Unit> {
        tokenStorage.clearToken()
        _authState.value = AuthState.Unauthenticated
        return AppResult.Success(Unit)
    }

    override fun observeAuthState(): Flow<AuthState> = _authState.asStateFlow()
}

Plattformprüfungen zur Laufzeit

// Wenn expect/actual übertrieben ist, Laufzeitprüfungen verwenden
enum class Platform {
    Android, iOS, Desktop, Web
}

expect val currentPlatform: Platform

// androidMain
actual val currentPlatform: Platform = Platform.Android

// iosMain
actual val currentPlatform: Platform = Platform.iOS

// desktopMain
actual val currentPlatform: Platform = Platform.Desktop

// wasmJsMain
actual val currentPlatform: Platform = Platform.Web

// Verwendung
@Composable
fun AdaptiveComponent() {
    when (currentPlatform) {
        Platform.Android -> AndroidSpecificUI()
        Platform.iOS -> IOSSpecificUI()
        Platform.Desktop -> DesktopSpecificUI()
        Platform.Web -> WebSpecificUI()
    }
}

Testen

Gemeinsame Tests (commonTest)

// core/common/src/commonTest/kotlin/ResultTest.kt
class ResultTest {
    @Test
    fun `map transformiert Erfolgswert`() {
        val result: AppResult<Int> = AppResult.Success(5)
        val mapped = result.map { it * 2 }

        assertTrue(mapped is AppResult.Success)
        assertEquals(10, (mapped as AppResult.Success).data)
    }

    @Test
    fun `map bewahrt Fehler`() {
        val result: AppResult<Int> = AppResult.Error("Testfehler")
        val mapped = result.map { it * 2 }

        assertTrue(mapped is AppResult.Error)
    }
}

Plattformspezifische Tests

// androidTest – verwendet Robolectric oder instrumentierte Tests
// iosTest – läuft auf Simulator
// jvmTest – standard JUnit

Best Practices

Gebote

  • So viel Code wie möglich in commonMain platzieren
  • expect/actual nur für Plattform-APIs verwenden
  • Plattformspezifische Implementierungen minimal halten
  • Dependency Injection für Plattformabhängigkeiten verwenden

Verbote

  • Keine plattformspezifischen Abhängigkeiten in gemeinsamen Schnittstellen
  • Keine expect-Deklarationen ohne entsprechende actual-Implementierungen
  • Keine Verwendung von Android-APIs in gemeinsamen Tests
  • Keine fest codierten Plattformprüfungen, wenn expect/actual ausreicht

Verwandte Skills