Learn-skills.dev jetpack-compose
Jetpack Compose patterns for declarative UI, state management, theming, animations, and performance optimization.
install
source · Clone the upstream repo
git clone https://github.com/NeverSight/learn-skills.dev
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/NeverSight/learn-skills.dev "$T" && mkdir -p ~/.claude/skills && cp -r "$T/data/skills-md/ahmed3elshaer/everything-claude-code-mobile/jetpack-compose" ~/.claude/skills/neversight-learn-skills-dev-jetpack-compose && rm -rf "$T"
manifest:
data/skills-md/ahmed3elshaer/everything-claude-code-mobile/jetpack-compose/SKILL.mdsource content
Jetpack Compose Patterns
Modern declarative UI patterns for Android.
State Management
State Hoisting
// ✅ CORRECT: Stateless composable @Composable fun Counter( count: Int, onIncrement: () -> Unit, modifier: Modifier = Modifier ) { Row(modifier = modifier) { Text("Count: $count") Button(onClick = onIncrement) { Text("+") } } } // Parent owns state @Composable fun CounterScreen() { var count by rememberSaveable { mutableStateOf(0) } Counter( count = count, onIncrement = { count++ } ) }
Remember Variants
// remember - Survives recomposition val alpha by remember { mutableStateOf(1f) } // rememberSaveable - Survives config change var count by rememberSaveable { mutableStateOf(0) } // remember with key - Resets on key change val animation = remember(itemId) { Animatable(0f) } // derivedStateOf - Computed, updates only when result changes val isValid by remember { derivedStateOf { email.isNotBlank() && password.length >= 8 } }
Composition Patterns
Slot API
@Composable fun AppBar( title: @Composable () -> Unit, navigationIcon: @Composable () -> Unit = {}, actions: @Composable RowScope.() -> Unit = {} ) { TopAppBar( title = { title() }, navigationIcon = { navigationIcon() }, actions = actions ) } // Usage AppBar( title = { Text("Home") }, navigationIcon = { IconButton(onClick = {}) { Icon(Icons.Default.Menu, null) } }, actions = { IconButton(onClick = {}) { Icon(Icons.Default.Search, null) } } )
Modifier Pattern
@Composable fun CustomButton( onClick: () -> Unit, modifier: Modifier = Modifier, // First optional parameter enabled: Boolean = true, content: @Composable RowScope.() -> Unit ) { Button( onClick = onClick, modifier = modifier, // Apply modifier first enabled = enabled, content = content ) }
Side Effects
LaunchedEffect
@Composable fun HomeScreen(viewModel: HomeViewModel) { // Runs once LaunchedEffect(Unit) { viewModel.loadData() } // Runs when key changes LaunchedEffect(userId) { viewModel.loadUser(userId) } }
DisposableEffect
@Composable fun LifecycleObserver(onResume: () -> Unit) { val lifecycleOwner = LocalLifecycleOwner.current DisposableEffect(lifecycleOwner) { val observer = LifecycleEventObserver { _, event -> if (event == Lifecycle.Event.ON_RESUME) onResume() } lifecycleOwner.lifecycle.addObserver(observer) onDispose { lifecycleOwner.lifecycle.removeObserver(observer) } } }
Theming
Material 3
@Composable fun AppTheme( darkTheme: Boolean = isSystemInDarkTheme(), content: @Composable () -> Unit ) { val colorScheme = if (darkTheme) DarkColorScheme else LightColorScheme MaterialTheme( colorScheme = colorScheme, typography = AppTypography, content = content ) } // Usage val backgroundColor = MaterialTheme.colorScheme.surface val textStyle = MaterialTheme.typography.bodyLarge
Lists
LazyColumn
LazyColumn { items( items = users, key = { it.id } // Critical for performance ) { user -> UserItem(user = user) } }
Animations
Animate Values
val alpha by animateFloatAsState( targetValue = if (visible) 1f else 0f, animationSpec = tween(durationMillis = 300) ) val size by animateDpAsState( targetValue = if (expanded) 200.dp else 100.dp )
AnimatedContent
AnimatedContent( targetState = state, transitionSpec = { fadeIn() togetherWith fadeOut() } ) { targetState -> when (targetState) { is Loading -> LoadingContent() is Success -> SuccessContent(targetState.data) is Error -> ErrorContent() } }
Remember: Compose is declarative. Describe the UI, don't command it.