Jetpack Compose
What This Does
Provides expert guidance for building native Android applications with Jetpack Compose — from composable design and Material Design 3 theming to state management, navigation, and Android platform integrations. Covers modern Android architecture with Kotlin, coroutines, and the Jetpack library suite.
Instructions
Assess the project. Determine:
- Minimum SDK version (affects available Compose APIs)
- New project or migrating from XML Views?
- Architecture: MVVM with ViewModel, MVI, or Compose-specific?
- DI framework: Hilt (recommended), Koin, or manual?
- Networking: Retrofit + OkHttp, Ktor, or other?
Project architecture (MVVM + Compose):
// Screen composable — UI layer
@Composable
fun TodoListScreen(
viewModel: TodoListViewModel = hiltViewModel()
) {
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
TodoListContent(
todos = uiState.todos,
isLoading = uiState.isLoading,
)
}
// Content composable — pure UI, easily previewable
@Composable
private fun TodoListContent(
todos: List<Todo>,
isLoading: Boolean,
onToggleTodo: (Todo) -> Unit,
onDeleteTodo: (Todo) -> Unit,
) {
Scaffold(
topBar = { TopAppBar(title = { Text("Todos") }) }
) { padding ->
if (isLoading) {
CircularProgressIndicator(modifier = Modifier.padding(padding))
} else {
LazyColumn(contentPadding = padding) {
items(todos, key = { it.id }) { todo ->
TodoItem(
todo = todo,
onToggleTodo(todo) },
onDeleteTodo(todo) },
)
}
}
}
}
}
// ViewModel — business logic
@HiltViewModel
class TodoListViewModel @Inject constructor(
private val repository: TodoRepository
) : ViewModel() {
private val _uiState = MutableStateFlow(TodoListUiState())
val uiState: StateFlow<TodoListUiState> = _uiState.asStateFlow()
init { loadTodos() }
private fun loadTodos() {
viewModelScope.launch {
_uiState.update { it.copy(isLoading = true) }
repository.getTodos()
.onSuccess { todos ->
_uiState.update { it.copy(todos = todos, isLoading = false) }
}
.onFailure { error ->
_uiState.update { it.copy(error = error.message, isLoading = false) }
}
}
}
}
// UI State — immutable data class
data class TodoListUiState(
val todos: List<Todo> = emptyList(),
val isLoading: Boolean = false,
val error: String? = null,
)
Material Design 3 theming:
@Composable
fun AppTheme(
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 = AppTypography,
content = content
)
}
Navigation with Compose Navigation:
// Type-safe navigation (Compose Navigation 2.8+)
@Serializable data object Home
@Serializable data class Detail(val id: String)
NavHost(navController, startDestination = Home) {
composable<Home> {
HomeScreen(onItemClick = { id ->
navController.navigate(Detail(id))
})
}
composable<Detail> { backStackEntry ->
val detail: Detail = backStackEntry.toRoute()
DetailScreen(itemId = detail.id)
}
}
State management patterns:
- Use
StateFlow + collectAsStateWithLifecycle() for ViewModel state
- Use
remember and rememberSaveable for local composable state
- Hoist state to the lowest common ancestor
- Use UiState data classes for screen-level state
- Use
derivedStateOf for computed values
Performance optimization:
- Use
key parameter in LazyColumn items for stable recomposition
- Use
remember for expensive computations
- Avoid allocations in composition (no object creation in composable body)
- Use
@Stable and @Immutable annotations for data classes
- Profile with Layout Inspector and Compose compiler metrics
- Defer reads with
derivedStateOf and lambda-based modifiers
Output Format
When generating Compose code:
- Kotlin with explicit types on public APIs
- Compose UI with Material 3 components
- Hilt for dependency injection
- Coroutines for async work
- Include necessary imports
- Follow Android Kotlin style guide
Tips
- Always separate Screen composables (with ViewModel) from Content composables (pure UI, previewable)
- Use
@Preview with multiple configurations (dark theme, large font, different screen sizes)
collectAsStateWithLifecycle() is lifecycle-aware — always prefer it over collectAsState()
- Use Compose BOM to keep all Compose library versions aligned
- Test composables with
createComposeRule() from compose-ui-test
- Enable strong skipping mode in the Compose compiler for better performance
- Use Coil for image loading in Compose — it has native Compose support
1---2name: jetpack-compose3description: Jetpack Compose for Android — Material Design 3, composable architecture, state management, and Android-specific best practices.4---56# Jetpack Compose78## What This Does910Provides expert guidance for building native Android applications with Jetpack Compose — from composable design and Material Design 3 theming to state management, navigation, and Android platform integrations. Covers modern Android architecture with Kotlin, coroutines, and the Jetpack library suite.1112## Instructions13141. **Assess the project.** Determine:15 - Minimum SDK version (affects available Compose APIs)16 - New project or migrating from XML Views?17 - Architecture: MVVM with ViewModel, MVI, or Compose-specific?18 - DI framework: Hilt (recommended), Koin, or manual?19 - Networking: Retrofit + OkHttp, Ktor, or other?20212. **Project architecture (MVVM + Compose):**22 ```kotlin23 // Screen composable — UI layer24 @Composable25 fun TodoListScreen(26 viewModel: TodoListViewModel = hiltViewModel()27 ) {28 val uiState by viewModel.uiState.collectAsStateWithLifecycle()2930 TodoListContent(31 todos = uiState.todos,32 isLoading = uiState.isLoading,33 onToggleTodo = viewModel::toggleTodo,34 onDeleteTodo = viewModel::deleteTodo,35 )36 }3738 // Content composable — pure UI, easily previewable39 @Composable40 private fun TodoListContent(41 todos: List<Todo>,42 isLoading: Boolean,43 onToggleTodo: (Todo) -> Unit,44 onDeleteTodo: (Todo) -> Unit,45 ) {46 Scaffold(47 topBar = { TopAppBar(title = { Text("Todos") }) }48 ) { padding ->49 if (isLoading) {50 CircularProgressIndicator(modifier = Modifier.padding(padding))51 } else {52 LazyColumn(contentPadding = padding) {53 items(todos, key = { it.id }) { todo ->54 TodoItem(55 todo = todo,56 onToggle = { onToggleTodo(todo) },57 onDelete = { onDeleteTodo(todo) },58 )59 }60 }61 }62 }63 }6465 // ViewModel — business logic66 @HiltViewModel67 class TodoListViewModel @Inject constructor(68 private val repository: TodoRepository69 ) : ViewModel() {7071 private val _uiState = MutableStateFlow(TodoListUiState())72 val uiState: StateFlow<TodoListUiState> = _uiState.asStateFlow()7374 init { loadTodos() }7576 private fun loadTodos() {77 viewModelScope.launch {78 _uiState.update { it.copy(isLoading = true) }79 repository.getTodos()80 .onSuccess { todos ->81 _uiState.update { it.copy(todos = todos, isLoading = false) }82 }83 .onFailure { error ->84 _uiState.update { it.copy(error = error.message, isLoading = false) }85 }86 }87 }88 }8990 // UI State — immutable data class91 data class TodoListUiState(92 val todos: List<Todo> = emptyList(),93 val isLoading: Boolean = false,94 val error: String? = null,95 )96 ```97983. **Material Design 3 theming:**99 ```kotlin100 @Composable101 fun AppTheme(102 darkTheme: Boolean = isSystemInDarkTheme(),103 dynamicColor: Boolean = true,104 content: @Composable () -> Unit105 ) {106 val colorScheme = when {107 dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {108 val context = LocalContext.current109 if (darkTheme) dynamicDarkColorScheme(context)110 else dynamicLightColorScheme(context)111 }112 darkTheme -> darkColorScheme()113 else -> lightColorScheme()114 }115116 MaterialTheme(117 colorScheme = colorScheme,118 typography = AppTypography,119 content = content120 )121 }122 ```1231244. **Navigation with Compose Navigation:**125 ```kotlin126 // Type-safe navigation (Compose Navigation 2.8+)127 @Serializable data object Home128 @Serializable data class Detail(val id: String)129130 NavHost(navController, startDestination = Home) {131 composable<Home> {132 HomeScreen(onItemClick = { id ->133 navController.navigate(Detail(id))134 })135 }136 composable<Detail> { backStackEntry ->137 val detail: Detail = backStackEntry.toRoute()138 DetailScreen(itemId = detail.id)139 }140 }141 ```1421435. **State management patterns:**144 - Use `StateFlow` + `collectAsStateWithLifecycle()` for ViewModel state145 - Use `remember` and `rememberSaveable` for local composable state146 - Hoist state to the lowest common ancestor147 - Use UiState data classes for screen-level state148 - Use `derivedStateOf` for computed values1491506. **Performance optimization:**151 - Use `key` parameter in `LazyColumn` items for stable recomposition152 - Use `remember` for expensive computations153 - Avoid allocations in composition (no object creation in composable body)154 - Use `@Stable` and `@Immutable` annotations for data classes155 - Profile with Layout Inspector and Compose compiler metrics156 - Defer reads with `derivedStateOf` and lambda-based modifiers157158## Output Format159160When generating Compose code:161- Kotlin with explicit types on public APIs162- Compose UI with Material 3 components163- Hilt for dependency injection164- Coroutines for async work165- Include necessary imports166- Follow Android Kotlin style guide167168## Tips169170- Always separate Screen composables (with ViewModel) from Content composables (pure UI, previewable)171- Use `@Preview` with multiple configurations (dark theme, large font, different screen sizes)172- `collectAsStateWithLifecycle()` is lifecycle-aware — always prefer it over `collectAsState()`173- Use Compose BOM to keep all Compose library versions aligned174- Test composables with `createComposeRule()` from compose-ui-test175- Enable strong skipping mode in the Compose compiler for better performance176- Use Coil for image loading in Compose — it has native Compose support