From antigravity-awesome-skills
Guides Android UI development with Jetpack Compose: project setup, ViewModel/StateFlow state management, navigation type-safety, performance optimization, Material Design 3. For new projects, XML migrations, complex UIs.
npx claudepluginhub sickn33/antigravity-awesome-skillsThis skill uses the workspace's default tool permissions.
A comprehensive guide for building production-quality Android applications using Jetpack Compose. This skill covers architectural patterns, state management with ViewModels, navigation type-safety, and performance optimization techniques.
Guides Android UI development with Jetpack Compose: project setup, ViewModel/StateFlow state management, navigation type-safety, performance optimization, Material Design 3. For new projects, XML migrations, complex UIs.
Guides building native Android UIs with Jetpack Compose, including state management via remember/mutableStateOf, state hoisting, and ViewModel integration.
Provides Jetpack Compose patterns for state hoisting, remember variants, slot APIs, modifiers, side effects, theming, animations, and performance in Android UI development.
Share bugs, ideas, or general feedback.
A comprehensive guide for building production-quality Android applications using Jetpack Compose. This skill covers architectural patterns, state management with ViewModels, navigation type-safety, and performance optimization techniques.
Ensure your libs.versions.toml includes the necessary Compose BOM and libraries.
[versions]
composeBom = "2024.02.01"
activityCompose = "1.8.2"
[libraries]
androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "composeBom" }
androidx-ui = { group = "androidx.compose.ui", name = "ui" }
androidx-ui-graphics = { group = "androidx.compose.ui", name = "ui-graphics" }
androidx-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview" }
androidx-material3 = { group = "androidx.compose.material3", name = "material3" }
androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "activityCompose" }
Use ViewModel with StateFlow to expose UI state. Avoid exposing MutableStateFlow.
// UI State Definition
data class UserUiState(
val isLoading: Boolean = false,
val user: User? = null,
val error: String? = null
)
// ViewModel
class UserViewModel @Inject constructor(
private val userRepository: UserRepository
) : ViewModel() {
private val _uiState = MutableStateFlow(UserUiState())
val uiState: StateFlow<UserUiState> = _uiState.asStateFlow()
fun loadUser() {
viewModelScope.launch {
_uiState.update { it.copy(isLoading = true) }
try {
val user = userRepository.getUser()
_uiState.update { it.copy(user = user, isLoading = false) }
} catch (e: Exception) {
_uiState.update { it.copy(error = e.message, isLoading = false) }
}
}
}
}
Consume the state in a "Screen" composable and pass data down to stateless components.
@Composable
fun UserScreen(
viewModel: UserViewModel = hiltViewModel()
) {
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
UserContent(
uiState = uiState,
onRetry = viewModel::loadUser
)
}
@Composable
fun UserContent(
uiState: UserUiState,
onRetry: () -> Unit
) {
Scaffold { padding ->
Box(modifier = Modifier.padding(padding)) {
when {
uiState.isLoading -> CircularProgressIndicator()
uiState.error != null -> ErrorView(uiState.error, onRetry)
uiState.user != null -> UserProfile(uiState.user)
}
}
}
}
Using the new Navigation Compose Type Safety (available in recent versions).
// Define Destinations
@Serializable
object Home
@Serializable
data class Profile(val userId: String)
// Setup NavHost
@Composable
fun AppNavHost(navController: NavHostController) {
NavHost(navController, startDestination = Home) {
composable<Home> {
HomeScreen(onNavigateToProfile = { id ->
navController.navigate(Profile(userId = id))
})
}
composable<Profile> { backStackEntry ->
val profile: Profile = backStackEntry.toRoute()
ProfileScreen(userId = profile.userId)
}
}
}
remember and derivedStateOf to minimize unnecessary calculations during recomposition.@Immutable or @Stable if they contain List or other unstable types to enable smart recomposition skipping.LaunchedEffect for one-off side effects (like showing a Snackbar) triggered by state changes.remember.ViewModel instances down to child components. Pass only the data (state) and lambda callbacks (events).Problem: Infinite Recomposition loop.
Solution: Check if you are creating new object instances (like List or Modifier) inside the composition without remember, or if you are updating state inside the composition phase instead of a side-effect or callback. Use Layout Inspector to debug recomposition counts.