Claude-skill-registry android-kotlin-development
Develop native Android apps with Kotlin. Covers MVVM with Jetpack, Compose for modern UI, Retrofit for API calls, Room for local storage, and navigation architecture.
install
source · Clone the upstream repo
git clone https://github.com/majiayu000/claude-skill-registry
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/android-kotlin-development" ~/.claude/skills/majiayu000-claude-skill-registry-android-kotlin-development && rm -rf "$T"
manifest:
skills/data/android-kotlin-development/SKILL.mdsource content
Android Kotlin Development
Overview
Build robust native Android applications using Kotlin with modern architecture patterns, Jetpack libraries, and Compose for declarative UI.
When to Use
- Creating native Android applications with best practices
- Using Kotlin for type-safe development
- Implementing MVVM architecture with Jetpack
- Building modern UIs with Jetpack Compose
- Integrating with Android platform APIs
Instructions
1. Models & API Service
// Models data class User( val id: String, val name: String, val email: String, val avatarUrl: String? = null ) data class Item( val id: String, val title: String, val description: String, val imageUrl: String? = null, val price: Double ) // API Service with Retrofit interface ApiService { @GET("/users/{id}") suspend fun getUser(@Path("id") userId: String): User @PUT("/users/{id}") suspend fun updateUser( @Path("id") userId: String, @Body user: User ): User @GET("/items") suspend fun getItems(@Query("filter") filter: String = "all"): List<Item> @POST("/items") suspend fun createItem(@Body item: Item): Item } // Network client setup @Module @InstallIn(SingletonComponent::class) object NetworkModule { @Provides @Singleton fun provideRetrofit(): Retrofit { val httpClient = OkHttpClient.Builder() .addInterceptor { chain -> val original = chain.request() val requestBuilder = original.newBuilder() val token = PreferencesManager.getToken() if (token.isNotEmpty()) { requestBuilder.addHeader("Authorization", "Bearer $token") } requestBuilder.addHeader("Content-Type", "application/json") chain.proceed(requestBuilder.build()) } .connectTimeout(30, TimeUnit.SECONDS) .readTimeout(30, TimeUnit.SECONDS) .build() return Retrofit.Builder() .baseUrl("https://api.example.com") .client(httpClient) .addConverterFactory(GsonConverterFactory.create()) .build() } @Provides @Singleton fun provideApiService(retrofit: Retrofit): ApiService { return retrofit.create(ApiService::class.java) } }
2. MVVM ViewModels with Jetpack
@HiltViewModel class UserViewModel @Inject constructor( private val apiService: ApiService ) : ViewModel() { private val _user = MutableStateFlow<User?>(null) val user: StateFlow<User?> = _user.asStateFlow() private val _isLoading = MutableStateFlow(false) val isLoading: StateFlow<Boolean> = _isLoading.asStateFlow() private val _errorMessage = MutableStateFlow<String?>(null) val errorMessage: StateFlow<String?> = _errorMessage.asStateFlow() fun fetchUser(userId: String) { viewModelScope.launch { _isLoading.value = true _errorMessage.value = null try { val user = apiService.getUser(userId) _user.value = user } catch (e: Exception) { _errorMessage.value = e.message ?: "Unknown error" } finally { _isLoading.value = false } } } fun logout() { _user.value = null } } @HiltViewModel class ItemsViewModel @Inject constructor( private val apiService: ApiService ) : ViewModel() { private val _items = MutableStateFlow<List<Item>>(emptyList()) val items: StateFlow<List<Item>> = _items.asStateFlow() private val _isLoading = MutableStateFlow(false) val isLoading: StateFlow<Boolean> = _isLoading.asStateFlow() fun fetchItems(filter: String = "all") { viewModelScope.launch { _isLoading.value = true try { val items = apiService.getItems(filter) _items.value = items } catch (e: Exception) { println("Error fetching items: ${e.message}") } finally { _isLoading.value = false } } } fun addItem(item: Item) { viewModelScope.launch { try { val created = apiService.createItem(item) _items.value = _items.value + created } catch (e: Exception) { println("Error creating item: ${e.message}") } } } }
3. Jetpack Compose UI
@Composable fun MainScreen() { val navController = rememberNavController() NavHost(navController = navController, startDestination = "home") { composable("home") { HomeScreen(navController) } composable("profile") { ProfileScreen(navController) } composable("details/{itemId}") { backStackEntry -> val itemId = backStackEntry.arguments?.getString("itemId") ?: return@composable DetailsScreen(itemId = itemId, navController = navController) } } } @Composable fun HomeScreen(navController: NavController) { val viewModel: ItemsViewModel = hiltViewModel() val items by viewModel.items.collectAsState() val isLoading by viewModel.isLoading.collectAsState() LaunchedEffect(Unit) { viewModel.fetchItems() } Scaffold( topBar = { TopAppBar(title = { Text("Items") }) } ) { paddingValues -> if (isLoading) { Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { CircularProgressIndicator() } } else { LazyColumn( modifier = Modifier .padding(paddingValues) .fillMaxSize(), contentPadding = PaddingValues(8.dp) ) { items(items) { item -> ItemCard( item = item, onClick = { navController.navigate("details/${item.id}") } ) } } } } } @Composable fun ItemCard(item: Item, onClick: () -> Unit) { Card( modifier = Modifier .fillMaxWidth() .padding(8.dp) .clickable { onClick() } ) { Row(modifier = Modifier.padding(16.dp)) { Column(modifier = Modifier.weight(1f)) { Text(text = item.title, style = MaterialTheme.typography.headlineSmall) Text(text = item.description, style = MaterialTheme.typography.bodyMedium) Text(text = "$${item.price}", style = MaterialTheme.typography.bodySmall) } Icon(imageVector = Icons.Default.ArrowForward, contentDescription = null) } } } @Composable fun ProfileScreen(navController: NavController) { val viewModel: UserViewModel = hiltViewModel() val user by viewModel.user.collectAsState() val isLoading by viewModel.isLoading.collectAsState() LaunchedEffect(Unit) { viewModel.fetchUser("current-user") } Scaffold( topBar = { TopAppBar(title = { Text("Profile") }) } ) { paddingValues -> if (isLoading) { Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { CircularProgressIndicator() } } else if (user != null) { Column( modifier = Modifier .padding(paddingValues) .fillMaxSize() .padding(16.dp) ) { Text(text = user!!.name, style = MaterialTheme.typography.headlineMedium) Text(text = user!!.email, style = MaterialTheme.typography.bodyMedium) Spacer(modifier = Modifier.height(24.dp)) Button( onClick = { viewModel.logout() }, modifier = Modifier.fillMaxWidth() ) { Text("Logout") } } } } } @Composable fun DetailsScreen(itemId: String, navController: NavController) { Scaffold( topBar = { TopAppBar( title = { Text("Details") }, navigationIcon = { IconButton(onClick = { navController.popBackStack() }) { Icon(Icons.Default.ArrowBack, contentDescription = "Back") } } ) } ) { paddingValues -> Column( modifier = Modifier .padding(paddingValues) .fillMaxSize() .padding(16.dp), horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.Center ) { Text("Item ID: $itemId", style = MaterialTheme.typography.headlineSmall) } } }
Best Practices
✅ DO
- Use Kotlin for all new Android code
- Implement MVVM with Jetpack libraries
- Use Jetpack Compose for UI development
- Leverage coroutines for async operations
- Use Room for local data persistence
- Implement proper error handling
- Use Hilt for dependency injection
- Use StateFlow for reactive state
- Test on multiple device types
- Follow Android design guidelines
❌ DON'T
- Store tokens in SharedPreferences
- Make network calls on main thread
- Ignore lifecycle management
- Skip null safety checks
- Hardcode strings and resources
- Ignore configuration changes
- Store passwords in code
- Deploy without device testing
- Use deprecated APIs
- Accumulate memory leaks