Skip to content
Back to skills

Dev Android Kotlin Advisor

ASecurity

Développement Android natif avec Kotlin et Jetpack Compose — architecture MVVM/MVI, UI Compose, Hilt, Room, Coroutines, Flow, tests, Gradle KTS, Play Store. Se déclenche avec "Android", "Kotlin", "Jetpack Compose", "Android Studio", "Gradle", "Room", "Hilt", "Coroutines", "Play Store". Also triggers on "Android with Kotlin", "MVVM on Android".

  • 27 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added October 18, 2026
ai-agentsgokotlinbashnodeapidatabase

Works with

  • cli
  • api

Security analysis

A100/100

Scanned October 18, 2026

npx -y skills add khalilbenaz/claude-skills-collection --skill dev-android-kotlin-advisor --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Dev Android Kotlin Advisor?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Dev Android Kotlin Advisor
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/khalilbenaz-dev-android-kotlin-advisor/badge)](https://www.skillsdirectory.com/skills/khalilbenaz-dev-android-kotlin-advisor)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: dev-android-kotlin-advisor
description: Développement Android natif avec Kotlin et Jetpack Compose — architecture MVVM/MVI, UI Compose, Hilt, Room, Coroutines, Flow, tests, Gradle KTS, Play Store. Se déclenche avec "Android", "Kotlin", "Jetpack Compose", "Android Studio", "Gradle", "Room", "Hilt", "Coroutines", "Play Store". Also triggers on "Android with Kotlin", "MVVM on Android".
---

# Android Kotlin Advisor

## 1. Choix d'architecture

| Contexte | Pattern recommandé |
|---|---|
| App simple, 1–3 écrans | MVVM + Repository (StateFlow) |
| App Compose multi-écrans | MVI (état immuable, `sealed class UiState`) |
| Large équipe / multi-feature | Clean Architecture + multi-module Gradle |

Structure recommandée multi-module :
```
app/
feature/home/
feature/profile/
core/data/
core/domain/
core/ui/
build-logic/               ← convention plugins partagés
```

## 2. UI avec Jetpack Compose

Toujours préférer les composables **stateless** (state hoisting) :

```kotlin
// BON : state hissé, composable testable et réutilisable
@Composable
fun LoginForm(
    state: LoginUiState,
    onEmailChange: (String) -> Unit,
    onSubmit: () -> Unit,
) { ... }

// MAUVAIS : état caché dans le composable, non testable
@Composable
fun LoginForm() {
    var email by remember { mutableStateOf("") }
    ...
}
```

Checklist Compose :
- Material 3 BOM : `implementation(platform("androidx.compose:compose-bom:2025.05.00"))`
- Dynamic Color (Android 12+) : `dynamicLightColorScheme(context)`
- Navigation : `NavHost` + `NavController`, arguments typés via `NavType`
- Éviter `LocalContext.current` profondément imbriqué → passer en paramètre

## 3. State management

```kotlin
// ViewModel pattern recommandé
@HiltViewModel
class HomeViewModel @Inject constructor(
    private val repo: PostRepository
) : ViewModel() {

    private val _uiState = MutableStateFlow<HomeUiState>(HomeUiState.Loading)
    val uiState: StateFlow<HomeUiState> = _uiState.asStateFlow()

    init { loadPosts() }

    private fun loadPosts() = viewModelScope.launch {
        repo.getPosts()
            .catch { _uiState.value = HomeUiState.Error(it.message) }
            .collect { _uiState.value = HomeUiState.Success(it) }
    }
}

sealed class HomeUiState {
    object Loading : HomeUiState()
    data class Success(val posts: List<Post>) : HomeUiState()
    data class Error(val msg: String?) : HomeUiState()
}
```

Collecte lifecycle-aware dans un composable :
```kotlin
val state by viewModel.uiState.collectAsStateWithLifecycle()
```

## 4. Injection de dépendances

**Hilt** (recommandé pour projets standard Google) :
```kotlin
// App
@HiltAndroidApp class App : Application()

// Fragment/Activity
@AndroidEntryPoint class HomeFragment : Fragment()

// Module
@Module @InstallIn(SingletonComponent::class)
object NetworkModule {
    @Provides @Singleton
    fun provideRetrofit(): Retrofit = Retrofit.Builder()
        .baseUrl(BuildConfig.BASE_URL)
        .build()
}
```

**Koin** (léger, sans génération de code) :
```kotlin
val appModule = module {
    singleOf(::UserRepository)
    viewModelOf(::HomeViewModel)
}
// App: startKoin { modules(appModule) }
```

Critère de choix : Hilt si AGP 8+, équipe > 3, besoin de validation compile-time. Koin si prototype, équipe Dagger-phobe, ou KMP envisagé.

## 5. Couche Data

**Room** (ORM local) :
```kotlin
@Entity data class Post(@PrimaryKey val id: Int, val title: String)

@Dao interface PostDao {
    @Query("SELECT * FROM post") fun getAll(): Flow<List<Post>>
    @Insert(onConflict = OnConflictStrategy.REPLACE) suspend fun insert(post: Post)
}

@Database(entities = [Post::class], version = 2)
abstract class AppDatabase : RoomDatabase() {
    abstract fun postDao(): PostDao
    // migration : addMigrations(MIGRATION_1_2)
}
```

**Retrofit + kotlinx.serialization** :
```kotlin
@Serializable data class PostDto(val id: Int, val title: String)

interface PostApi {
    @GET("posts") suspend fun getPosts(): List<PostDto>
}
// OkHttp : ajouter HttpLoggingInterceptor en DEBUG seulement
```

**DataStore** (remplace SharedPreferences) :
```kotlin
val Context.dataStore by preferencesDataStore("settings")
val DARK_MODE = booleanPreferencesKey("dark_mode")
// write: dataStore.edit { it[DARK_MODE] = true }
// read:  dataStore.data.map { it[DARK_MODE] ?: false }
```

## 6. Concurrence — Coroutines & Flow

```kotlin
// NE PAS utiliser GlobalScope
// NE PAS bloquer avec runBlocking dans le code de prod

// Dispatchers : IO pour réseau/disk, Default pour CPU, Main pour UI
viewModelScope.launch(Dispatchers.IO) {
    val result = api.getPosts()      // suspend fun
    withContext(Dispatchers.Main) { /* màj UI */ }
}

// Canal one-shot (navigation, snackbar)
private val _events = Channel<UiEvent>(Channel.BUFFERED)
val events = _events.receiveAsFlow()
// emit: _events.send(UiEvent.NavigateToDetail(id))
```

## 7. Tests

```kotlin
// Unit test ViewModel avec Turbine + MockK
@Test fun `loading posts emits Success state`() = runTest {
    val repo = mockk<PostRepository>()
    every { repo.getPosts() } returns flowOf(listOf(Post(1, "titre")))
    val vm = HomeViewModel(repo)
    vm.uiState.test {
        assertEquals(HomeUiState.Loading, awaitItem())
        assertTrue(awaitItem() is HomeUiState.Success)
        cancelAndIgnoreRemainingEvents()
    }
}

// Compose UI test
@get:Rule val composeRule = createComposeRule()

@Test fun `login button is disabled when email is empty`() {
    composeRule.setContent { LoginForm(state = LoginUiState(), ...) }
    composeRule.onNodeWithTag("btn_submit").assertIsNotEnabled()
}
```

Dépendances test :
```kotlin
testImplementation("io.mockk:mockk:1.13.x")
testImplementation("app.cash.turbine:turbine:1.x")
testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.8.x")
androidTestImplementation("androidx.compose.ui:ui-test-junit4")
```

## 8. Gradle KTS & build

Convention plugin partagé (`build-logic/`) :
```kotlin
// build-logic/src/.../AndroidFeatureConventionPlugin.kt
class AndroidFeatureConventionPlugin : Plugin<Project> {
    override fun apply(target: Project) = with(target) {
        pluginManager.apply("com.android.library")
        pluginManager.apply("org.jetbrains.kotlin.android")
        extensions.configure<LibraryExtension> {
            compileSdk = 35
            defaultConfig.minSdk = 24
        }
    }
}
```

Signature en CI (ne jamais committer le keystore) :
```bash
# Variables CI : KEY_ALIAS, KEY_PASSWORD, STORE_PASSWORD, KEYSTORE_BASE64
echo "$KEYSTORE_BASE64" | base64 -d > release.jks
```

## Garde-fous / Anti-patterns

| Anti-pattern | Correctif |
|---|---|
| Context dans un Singleton/ViewModel | Utiliser `applicationContext` ou injecter via Hilt `@ApplicationContext` |
| Recomposition excessive | Wrapper lambda en `remember { {} }`, utiliser `derivedStateOf` pour les calculs coûteux |
| `runBlocking` en prod | Toujours `launch` ou `async` dans un scope approprié |
| Mutation d'état UI depuis le thread IO | Toujours `withContext(Dispatchers.Main)` ou `StateFlow` |
| SharedPreferences dans code Compose | Migrer vers DataStore |
| Hardcoder les URLs / clés API | `BuildConfig` + variables CI / secrets manager |
| Ignorer les migrations Room | Toujours définir `Migration(oldV, newV)`, ne jamais utiliser `fallbackToDestructiveMigration()` en prod |
| Fragment backstack manuel en Compose | Déléguer entièrement à `navigation-compose` |

## Versions de référence (2026)

- Kotlin : **2.0.x**
- AGP : **8.5.x**
- Compose BOM : **2025.05.00**
- `compileSdk` / `targetSdk` : **35**
- `minSdk` recommandé : **24** (Android 7.0, ~97 % devices)
- Lifecycle / ViewModel : **2.8.x**
- Hilt : **2.51.x**
- Room : **2.7.x**
- Coroutines : **1.8.x**


## Communication Rules — MANDATORY

- Ultra-concise. No filler, no preamble, no pleasantries.
- Never say "happy to help", "sure!", "great question", "let me", or similar.
- Tool first, talk second. Act before explaining.
- Result first. Lead with outcome, not process.
- Stop when done. No summary, no recap, no trailing commentary.
- No politeness wrappers. Direct and blunt.
- Minimum words. If one word works, do not use ten.
- No unsolicited explanations.
- No emoji unless asked.

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.