android-development
Create production-quality Android applications following Google's official architecture guidance and NowInAndroid best practices. Use when building Android apps with Kotlin, Jetpack Compose, MVVM architecture, Hilt dependency injection, Room database, or multi-module projects. Triggers on requests to create Android projects, screens, ViewModels, repositories, feature modules, or when asked about Android architecture patterns.
git clone https://github.com/dpconde/claude-android-skill
git clone --depth=1 https://github.com/dpconde/claude-android-skill ~/.claude/skills/dpconde-claude-android-skill-android-development
SKILL.mdAndroid Development
Build Android applications following Google's official architecture guidance, as demonstrated in the NowInAndroid reference app.
Quick Reference
| Task | Reference File |
|---|---|
| Project structure & modules | modularization.md |
| Architecture layers (UI, Domain, Data) | architecture.md |
| Jetpack Compose patterns | compose-patterns.md |
| Gradle & build configuration | gradle-setup.md |
| Testing approach | testing.md |
Workflow Decision Tree
Creating a new project? → Read modularization.md for project structure → Use templates in
assets/templates/
Adding a new feature? → Create feature module with
api and impl submodules
→ Follow patterns in architecture.md
Building UI screens? → Read compose-patterns.md → Create Screen + ViewModel + UiState
Setting up data layer? → Read data layer section in architecture.md → Create Repository + DataSource + DAO
Core Principles
- Offline-first: Local database is source of truth, sync with remote
- Unidirectional data flow: Events flow down, data flows up
- Reactive streams: Use Kotlin Flow for all data exposure
- Modular by feature: Each feature is self-contained with clear boundaries
- Testable by design: Use interfaces and test doubles, no mocking libraries
Architecture Layers
┌─────────────────────────────────────────┐ │ UI Layer │ │ (Compose Screens + ViewModels) │ ├─────────────────────────────────────────┤ │ Domain Layer │ │ (Use Cases - optional, for reuse) │ ├─────────────────────────────────────────┤ │ Data Layer │ │ (Repositories + DataSources) │ └─────────────────────────────────────────┘
Module Types
app/ # App module - navigation, scaffolding feature/ ├── featurename/ │ ├── api/ # Navigation keys (public) │ └── impl/ # Screen, ViewModel, DI (internal) core/ ├── data/ # Repositories ├── database/ # Room DAOs, entities ├── network/ # Retrofit, API models ├── model/ # Domain models (pure Kotlin) ├── common/ # Shared utilities ├── ui/ # Reusable Compose components ├── designsystem/ # Theme, icons, base components ├── datastore/ # Preferences storage └── testing/ # Test utilities
Creating a New Feature
- Create
module with navigation keyfeature:myfeature:api - Create
module with:feature:myfeature:impl
- Composable UIMyFeatureScreen.kt
- State holderMyFeatureViewModel.kt
- Sealed interface for statesMyFeatureUiState.kt
- Navigation setupMyFeatureNavigation.kt
- Hilt DI moduleMyFeatureModule.kt
Standard File Patterns
ViewModel Pattern
@HiltViewModel class MyFeatureViewModel @Inject constructor( private val myRepository: MyRepository, ) : ViewModel() { val uiState: StateFlow<MyFeatureUiState> = myRepository .getData() .map { data -> MyFeatureUiState.Success(data) } .stateIn( scope = viewModelScope, started = SharingStarted.WhileSubscribed(5_000), initialValue = MyFeatureUiState.Loading, ) fun onAction(action: MyFeatureAction) { when (action) { is MyFeatureAction.ItemClicked -> handleItemClick(action.id) } } }
UiState Pattern
sealed interface MyFeatureUiState { data object Loading : MyFeatureUiState data class Success(val items: List<Item>) : MyFeatureUiState data class Error(val message: String) : MyFeatureUiState }
Screen Pattern
@Composable internal fun MyFeatureRoute( onNavigateToDetail: (String) -> Unit, viewModel: MyFeatureViewModel = hiltViewModel(), ) { val uiState by viewModel.uiState.collectAsStateWithLifecycle() MyFeatureScreen( uiState = uiState, onAction = viewModel::onAction, onNavigateToDetail = onNavigateToDetail, ) } @Composable internal fun MyFeatureScreen( uiState: MyFeatureUiState, onAction: (MyFeatureAction) -> Unit, onNavigateToDetail: (String) -> Unit, ) { when (uiState) { is MyFeatureUiState.Loading -> LoadingIndicator() is MyFeatureUiState.Success -> ContentList(uiState.items, onAction) is MyFeatureUiState.Error -> ErrorMessage(uiState.message) } }
Repository Pattern
interface MyRepository { fun getData(): Flow<List<MyModel>> suspend fun updateItem(id: String, data: MyModel) } internal class OfflineFirstMyRepository @Inject constructor( private val localDataSource: MyDao, private val networkDataSource: MyNetworkApi, ) : MyRepository { override fun getData(): Flow<List<MyModel>> = localDataSource.getAll().map { entities -> entities.map { it.toModel() } } override suspend fun updateItem(id: String, data: MyModel) { localDataSource.upsert(data.toEntity()) } }
Key Dependencies
// Gradle version catalog (libs.versions.toml) [versions] kotlin = "1.9.x" compose-bom = "2024.x.x" hilt = "2.48" room = "2.6.x" coroutines = "1.7.x" [libraries] androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "compose-bom" } hilt-android = { group = "com.google.dagger", name = "hilt-android", version.ref = "hilt" } room-runtime = { group = "androidx.room", name = "room-runtime", version.ref = "room" }
Build Configuration
Use convention plugins in
build-logic/ for consistent configuration:
- App modulesAndroidApplicationConventionPlugin
- Library modulesAndroidLibraryConventionPlugin
- Feature modulesAndroidFeatureConventionPlugin
- Compose setupAndroidComposeConventionPlugin
- Hilt setupAndroidHiltConventionPlugin
See gradle-setup.md for complete build configuration.