android-development
Android development with Kotlin, Jetpack Compose, and modern Android architecture. Use when building Android apps, implementing Material Design, or following Android best practices.
Best use case
android-development is best used when you need a repeatable AI agent workflow instead of a one-off prompt.
Android development with Kotlin, Jetpack Compose, and modern Android architecture. Use when building Android apps, implementing Material Design, or following Android best practices.
Teams using android-development should expect a more consistent output, faster repeated execution, less prompt rewriting.
When to use this skill
- You want a reusable workflow that can be run more than once with consistent structure.
When not to use this skill
- You only need a quick one-off answer and do not need a reusable workflow.
- You cannot install or maintain the underlying files, dependencies, or repository context.
Installation
Claude Code / Cursor / Codex
Manual Installation
- Download SKILL.md from GitHub
- Place it in
.claude/skills/android-development/SKILL.mdinside your project - Restart your AI agent — it will auto-discover the skill
How android-development Compares
| Feature / Agent | android-development | Standard Approach |
|---|---|---|
| Platform Support | Not specified | Limited / Varies |
| Context Awareness | High | Baseline |
| Installation Complexity | Unknown | N/A |
Frequently Asked Questions
What does this skill do?
Android development with Kotlin, Jetpack Compose, and modern Android architecture. Use when building Android apps, implementing Material Design, or following Android best practices.
Where can I find the source code?
You can find the source code on GitHub using the link provided at the top of the page.
SKILL.md Source
# Android Development
Comprehensive guide for building modern Android applications.
## Platforms Covered
| Platform | Min SDK | Target SDK |
| -------------- | ------------ | ----------- |
| Android Phone | API 24 (7.0) | API 34 (14) |
| Android Tablet | API 24 | API 34 |
| Android TV | API 24 | API 34 |
| Wear OS | API 30 | API 34 |
| Android Auto | API 29 | API 34 |
---
## Jetpack Compose (Modern UI)
### Basic Structure
```kotlin
@Composable
fun MyApp() {
MaterialTheme {
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background
) {
MainScreen()
}
}
}
@Composable
fun MainScreen() {
var count by remember { mutableStateOf(0) }
Column(
modifier = Modifier
.fillMaxSize()
.padding(16.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
Text(
text = "Count: $count",
style = MaterialTheme.typography.headlineLarge
)
Spacer(modifier = Modifier.height(16.dp))
Button(onClick = { count++ }) {
Text("Increment")
}
}
}
```
### State Management
```kotlin
// Local state
var text by remember { mutableStateOf("") }
// State hoisting
@Composable
fun StatefulCounter() {
var count by remember { mutableStateOf(0) }
StatelessCounter(count = count, onIncrement = { count++ })
}
@Composable
fun StatelessCounter(count: Int, onIncrement: () -> Unit) {
Button(onClick = onIncrement) {
Text("Count: $count")
}
}
// ViewModel state
@HiltViewModel
class MainViewModel @Inject constructor(
private val repository: ItemRepository
) : ViewModel() {
private val _uiState = MutableStateFlow(UiState())
val uiState: StateFlow<UiState> = _uiState.asStateFlow()
fun loadItems() {
viewModelScope.launch {
_uiState.update { it.copy(isLoading = true) }
try {
val items = repository.getItems()
_uiState.update { it.copy(items = items, isLoading = false) }
} catch (e: Exception) {
_uiState.update { it.copy(error = e.message, isLoading = false) }
}
}
}
}
// Collecting in Compose
@Composable
fun MainScreen(viewModel: MainViewModel = hiltViewModel()) {
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
when {
uiState.isLoading -> LoadingIndicator()
uiState.error != null -> ErrorMessage(uiState.error!!)
else -> ItemList(uiState.items)
}
}
```
### Navigation
```kotlin
// Navigation setup
@Composable
fun AppNavigation() {
val navController = rememberNavController()
NavHost(navController = navController, startDestination = "home") {
composable("home") {
HomeScreen(
onNavigateToDetail = { id ->
navController.navigate("detail/$id")
}
)
}
composable(
route = "detail/{itemId}",
arguments = listOf(navArgument("itemId") { type = NavType.StringType })
) { backStackEntry ->
val itemId = backStackEntry.arguments?.getString("itemId")
DetailScreen(itemId = itemId)
}
}
}
// Type-safe navigation (recommended)
@Serializable
data class DetailRoute(val itemId: String)
navController.navigate(DetailRoute(itemId = "123"))
```
### Lists
```kotlin
@Composable
fun ItemList(items: List<Item>) {
LazyColumn(
modifier = Modifier.fillMaxSize(),
contentPadding = PaddingValues(16.dp),
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
items(
items = items,
key = { it.id }
) { item ->
ItemCard(item = item)
}
}
}
// Pull to refresh
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun RefreshableList(
items: List<Item>,
isRefreshing: Boolean,
onRefresh: () -> Unit
) {
val pullRefreshState = rememberPullToRefreshState()
PullToRefreshBox(
isRefreshing = isRefreshing,
onRefresh = onRefresh,
state = pullRefreshState
) {
LazyColumn { /* content */ }
}
}
```
---
## Modern Android Architecture
### Clean Architecture Layers
```
app/
├── data/
│ ├── local/
│ │ ├── AppDatabase.kt
│ │ └── ItemDao.kt
│ ├── remote/
│ │ ├── ApiService.kt
│ │ └── ItemDto.kt
│ └── repository/
│ └── ItemRepositoryImpl.kt
├── domain/
│ ├── model/
│ │ └── Item.kt
│ ├── repository/
│ │ └── ItemRepository.kt
│ └── usecase/
│ └── GetItemsUseCase.kt
├── presentation/
│ ├── home/
│ │ ├── HomeScreen.kt
│ │ └── HomeViewModel.kt
│ └── navigation/
│ └── AppNavigation.kt
└── di/
└── AppModule.kt
```
### Dependency Injection (Hilt)
```kotlin
@HiltAndroidApp
class MyApplication : Application()
@Module
@InstallIn(SingletonComponent::class)
object AppModule {
@Provides
@Singleton
fun provideDatabase(@ApplicationContext context: Context): AppDatabase {
return Room.databaseBuilder(
context,
AppDatabase::class.java,
"app_database"
).build()
}
@Provides
@Singleton
fun provideApiService(): ApiService {
return Retrofit.Builder()
.baseUrl("https://api.example.com/")
.addConverterFactory(GsonConverterFactory.create())
.build()
.create(ApiService::class.java)
}
@Provides
@Singleton
fun provideItemRepository(
apiService: ApiService,
database: AppDatabase
): ItemRepository {
return ItemRepositoryImpl(apiService, database.itemDao())
}
}
```
---
## Data Layer
### Room Database
```kotlin
@Entity(tableName = "items")
data class ItemEntity(
@PrimaryKey val id: String,
val name: String,
val createdAt: Long
)
@Dao
interface ItemDao {
@Query("SELECT * FROM items ORDER BY createdAt DESC")
fun getItems(): Flow<List<ItemEntity>>
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertItems(items: List<ItemEntity>)
@Delete
suspend fun deleteItem(item: ItemEntity)
}
@Database(entities = [ItemEntity::class], version = 1)
abstract class AppDatabase : RoomDatabase() {
abstract fun itemDao(): ItemDao
}
```
### Retrofit API
```kotlin
interface ApiService {
@GET("items")
suspend fun getItems(): List<ItemDto>
@POST("items")
suspend fun createItem(@Body item: CreateItemRequest): ItemDto
@DELETE("items/{id}")
suspend fun deleteItem(@Path("id") id: String)
}
data class ItemDto(
val id: String,
val name: String,
@SerializedName("created_at") val createdAt: String
)
```
### Repository Pattern
```kotlin
interface ItemRepository {
fun getItems(): Flow<List<Item>>
suspend fun refreshItems()
suspend fun deleteItem(id: String)
}
class ItemRepositoryImpl @Inject constructor(
private val apiService: ApiService,
private val itemDao: ItemDao
) : ItemRepository {
override fun getItems(): Flow<List<Item>> {
return itemDao.getItems().map { entities ->
entities.map { it.toDomain() }
}
}
override suspend fun refreshItems() {
val items = apiService.getItems()
itemDao.insertItems(items.map { it.toEntity() })
}
}
```
---
## Material Design 3
### Theme Setup
```kotlin
@Composable
fun MyAppTheme(
darkTheme: Boolean = isSystemInDarkTheme(),
dynamicColor: Boolean = true,
content: @Composable () -> Unit
) {
val colorScheme = when {
dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
val context = LocalContext.current
if (darkTheme) dynamicDarkColorScheme(context)
else dynamicLightColorScheme(context)
}
darkTheme -> DarkColorScheme
else -> LightColorScheme
}
MaterialTheme(
colorScheme = colorScheme,
typography = Typography,
content = content
)
}
```
### Common Components
```kotlin
// Top App Bar
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun MyTopBar(
title: String,
onBackClick: () -> Unit
) {
TopAppBar(
title = { Text(title) },
navigationIcon = {
IconButton(onClick = onBackClick) {
Icon(Icons.Default.ArrowBack, contentDescription = "Back")
}
},
actions = {
IconButton(onClick = { /* menu */ }) {
Icon(Icons.Default.MoreVert, contentDescription = "Menu")
}
}
)
}
// Bottom Navigation
@Composable
fun MyBottomBar(
selectedTab: Int,
onTabSelected: (Int) -> Unit
) {
NavigationBar {
NavigationBarItem(
icon = { Icon(Icons.Default.Home, contentDescription = null) },
label = { Text("Home") },
selected = selectedTab == 0,
onClick = { onTabSelected(0) }
)
NavigationBarItem(
icon = { Icon(Icons.Default.Settings, contentDescription = null) },
label = { Text("Settings") },
selected = selectedTab == 1,
onClick = { onTabSelected(1) }
)
}
}
```
---
## Platform-Specific
### Android TV
```kotlin
// Focus management
@Composable
fun TvButton(
onClick: () -> Unit,
content: @Composable () -> Unit
) {
var isFocused by remember { mutableStateOf(false) }
Box(
modifier = Modifier
.onFocusChanged { isFocused = it.isFocused }
.focusable()
.clickable(onClick = onClick)
.background(
if (isFocused) MaterialTheme.colorScheme.primary
else MaterialTheme.colorScheme.surface
)
) {
content()
}
}
```
### Wear OS
```kotlin
@Composable
fun WearApp() {
MaterialTheme {
ScalingLazyColumn(
modifier = Modifier.fillMaxSize(),
anchorType = ScalingLazyListAnchorType.ItemCenter
) {
item { TimeText() }
item {
Chip(
onClick = { },
label = { Text("Action") }
)
}
}
}
}
```
---
## Testing
### Unit Tests
```kotlin
@Test
fun `getItems returns mapped domain objects`() = runTest {
val repository = ItemRepositoryImpl(
apiService = FakeApiService(),
itemDao = FakeItemDao()
)
val items = repository.getItems().first()
assertEquals(2, items.size)
assertEquals("Item 1", items[0].name)
}
```
### Compose UI Tests
```kotlin
@HiltAndroidTest
class MainScreenTest {
@get:Rule
val composeTestRule = createAndroidComposeRule<MainActivity>()
@Test
fun displayItems_whenLoaded() {
composeTestRule.onNodeWithText("Item 1").assertIsDisplayed()
composeTestRule.onNodeWithText("Item 2").assertIsDisplayed()
}
@Test
fun navigateToDetail_onItemClick() {
composeTestRule.onNodeWithText("Item 1").performClick()
composeTestRule.onNodeWithText("Item Details").assertIsDisplayed()
}
}
```
---
## Play Store Requirements
### Required
- Privacy policy
- App icon (512x512)
- Feature graphic (1024x500)
- Screenshots (min 2)
- Target API 34+
### App Bundle
```groovy
android {
bundle {
language {
enableSplit = true
}
density {
enableSplit = true
}
abi {
enableSplit = true
}
}
}
```
---
## Best Practices
### DO:
- Use Kotlin Coroutines and Flow
- Follow unidirectional data flow
- Use Jetpack Compose for new UI
- Implement proper lifecycle handling
- Support multiple screen sizes
### DON'T:
- Block main thread
- Leak contexts
- Hardcode dimensions
- Ignore process death
- Skip ProGuard rules
---
## Kotlin Multiplatform (KMP) / Compose Multiplatform (CMP)
### Shared Business Logic with KMP
```kotlin
// shared/src/commonMain/kotlin/UserRepository.kt
expect class PlatformContext
class UserRepository(private val api: ApiService, private val db: Database) {
suspend fun getUsers(): List<User> {
return try {
val remote = api.fetchUsers()
db.saveUsers(remote)
remote
} catch (e: Exception) {
db.getCachedUsers()
}
}
}
// shared/src/androidMain/kotlin/PlatformContext.kt
actual class PlatformContext(val context: Context)
// shared/src/iosMain/kotlin/PlatformContext.kt
actual class PlatformContext
```
### Compose Multiplatform (Shared UI)
```kotlin
// shared/src/commonMain/kotlin/App.kt
@Composable
fun App() {
MaterialTheme {
var users by remember { mutableStateOf<List<User>>(emptyList()) }
val repository = remember { UserRepository() }
LaunchedEffect(Unit) {
users = repository.getUsers()
}
LazyColumn {
items(users) { user ->
UserCard(user)
}
}
}
}
// Runs natively on Android, iOS, Desktop, and Web
```
### KMP Project Structure
```
project/
├── shared/
│ └── src/
│ ├── commonMain/ # Shared Kotlin code
│ ├── androidMain/ # Android-specific
│ ├── iosMain/ # iOS-specific
│ └── desktopMain/ # Desktop-specific
├── androidApp/ # Android entry point
├── iosApp/ # iOS entry point (Swift/SwiftUI)
└── desktopApp/ # Desktop entry point
```
---
## Navigation 3 (Type-Safe Navigation)
```kotlin
// Define routes as serializable data classes
@Serializable
data object Home
@Serializable
data class Detail(val itemId: String)
@Serializable
data class Settings(val section: String? = null)
// Navigation setup
@Composable
fun AppNavHost(navController: NavHostController = rememberNavController()) {
NavHost(navController = navController, startDestination = Home) {
composable<Home> {
HomeScreen(onItemClick = { id ->
navController.navigate(Detail(itemId = id))
})
}
composable<Detail> { backStackEntry ->
val detail: Detail = backStackEntry.toRoute()
DetailScreen(itemId = detail.itemId)
}
composable<Settings> { backStackEntry ->
val settings: Settings = backStackEntry.toRoute()
SettingsScreen(section = settings.section)
}
}
}
```
---
## API 35 (Android 15) Features
| Feature | Description |
| -------------------------- | ----------------------------------------------------- |
| **Edge-to-edge enforced** | Apps must handle insets properly |
| **Predictive back** | System back gesture with preview animations |
| **Private Space** | User-controlled hidden app profile |
| **Satellite connectivity** | SMS/MMS over satellite |
| **App archival** | Auto-archive unused apps, preserve data |
| **Health Connect** | Updated health data APIs |
```kotlin
// Edge-to-edge (required on API 35+)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContent {
Scaffold(
modifier = Modifier.fillMaxSize(),
contentWindowInsets = ScaffoldDefaults.contentWindowInsets,
) { innerPadding ->
MainContent(modifier = Modifier.padding(innerPadding))
}
}
}
// Predictive back with Compose
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun DetailScreen(onBack: () -> Unit) {
BackHandler(onBack = onBack)
// Content renders with predictive back animation automatically
}
```
---
## Baseline Profiles for Startup Performance
```kotlin
// baselineprofile/build.gradle.kts
dependencies {
implementation("androidx.benchmark:benchmark-macro-junit4:1.3.0")
}
// BaselineProfileGenerator.kt
@RunWith(AndroidJUnit4::class)
class BaselineProfileGenerator {
@get:Rule
val rule = BaselineProfileRule()
@Test
fun generateBaselineProfile() {
rule.collect(packageName = "com.example.app") {
// Critical user journey
pressHome()
startActivityAndWait()
device.findObject(By.text("Login")).click()
device.wait(Until.hasObject(By.text("Dashboard")), 5000)
}
}
}
```
```groovy
// app/build.gradle.kts
android {
buildTypes {
release {
// Baseline profiles improve startup by 15-30%
baselineProfile.automaticGenerationDuringBuild = true
}
}
}
```
---
## Credential Manager API
```kotlin
// Modern sign-in replacing legacy APIs
val credentialManager = CredentialManager.create(context)
// Sign in with passkeys, passwords, or federated identity
suspend fun signIn() {
val request = GetCredentialRequest(listOf(
GetPasswordOption(),
GetPublicKeyCredentialOption(requestJson = passkeyRequestJson),
GetGoogleIdOption(serverClientId = WEB_CLIENT_ID),
))
try {
val result = credentialManager.getCredential(context, request)
handleSignIn(result)
} catch (e: GetCredentialException) {
handleSignInError(e)
}
}
// Create a passkey
suspend fun createPasskey() {
val request = CreatePublicKeyCredentialRequest(
requestJson = createPasskeyRequestJson
)
val result = credentialManager.createCredential(context, request)
handlePasskeyCreation(result)
}
```Related Skills
svelte-development
Svelte 5 development with runes ($state, $derived, $effect), SvelteKit full-stack framework, and modern reactive patterns. Use when building Svelte applications, implementing fine-grained reactivity, or working with SvelteKit routing and server functions.
serverless-development
AWS Lambda, Vercel Edge Functions, Cloudflare Workers, cold starts, deployment patterns, and infrastructure as code (SST, Serverless Framework). Use when building serverless applications or optimizing function-based architectures.
pwa-development
Progressive Web App development for installable, offline-capable web applications. Use when building PWAs, implementing service workers, or creating offline-first experiences.
llm-app-development
LLM app development with RAG, prompt engineering, vector databases, and AI agents
ios-development
iOS, iPadOS, and tvOS development with Swift, SwiftUI, and UIKit. Use when building Apple platform apps, implementing iOS-specific features, or following Apple Human Interface Guidelines.
game-development
Game development with Unity, Unreal Engine, and Godot. Use when building games, implementing game mechanics, physics, AI, or working with game engines.
flutter-development
Cross-platform development with Flutter and Dart for iOS, Android, Web, Desktop, and embedded. Use when building Flutter apps, implementing Material/Cupertino design, or optimizing Dart code.
example-skill
Example skill - replace with your skill's description and activation keywords
websockets-realtime
Real-time communication with WebSockets, Server-Sent Events, and related technologies. Use when building chat, live updates, collaborative features, or any real-time functionality.
video-production
Professional video production from planning to delivery. Use when creating video content, editing workflows, motion graphics, or optimizing video for different platforms.
ui-research
Research-first UI/UX design workflow. Use BEFORE any frontend visual work to research modern patterns, gather inspiration from real products, and avoid generic AI-generated looks. Mandatory prerequisite for quality UI work.
ui-animation
Motion design and animation for user interfaces. Use when creating micro-interactions, page transitions, loading states, or any UI animation across web and mobile platforms.