mobile-android-design

Android Mobile Design

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "mobile-android-design" with this command: npx skills add ravinani02/opencode-agent-skills/ravinani02-opencode-agent-skills-mobile-android-design

Android Mobile Design

Master Material Design 3 (Material You) and Jetpack Compose to build modern, adaptive Android applications that integrate seamlessly with the Android ecosystem.

When to Use This Skill

  • Designing Android app interfaces following Material Design 3

  • Building Jetpack Compose UI and layouts

  • Implementing Android navigation patterns (Navigation Compose)

  • Creating adaptive layouts for phones, tablets, and foldables

  • Using Material 3 theming with dynamic colors

  • Building accessible Android interfaces

  • Implementing Android-specific gestures and interactions

  • Designing for different screen configurations

Core Concepts

  1. Material Design 3 Principles

Personalization: Dynamic color adapts UI to user's wallpaper Accessibility: Tonal palettes ensure sufficient color contrast Large Screens: Responsive layouts for tablets and foldables

Material Components:

  • Cards, Buttons, FABs, Chips

  • Navigation (rail, drawer, bottom nav)

  • Text fields, Dialogs, Sheets

  • Lists, Menus, Progress indicators

  1. Jetpack Compose Layout System

Column and Row:

// Vertical arrangement with alignment Column( modifier = Modifier.padding(16.dp), verticalArrangement = Arrangement.spacedBy(12.dp), horizontalAlignment = Alignment.Start ) { Text( text = "Title", style = MaterialTheme.typography.headlineSmall ) Text( text = "Subtitle", style = MaterialTheme.typography.bodyMedium, color = MaterialTheme.colorScheme.onSurfaceVariant ) }

// Horizontal arrangement with weight Row( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically ) { Icon(Icons.Default.Star, contentDescription = null) Text("Featured") Spacer(modifier = Modifier.weight(1f)) TextButton(onClick = {}) { Text("View All") } }

Lazy Lists and Grids:

// Lazy column with sticky headers LazyColumn { items.groupBy { it.category }.forEach { (category, categoryItems) -> stickyHeader { Text( text = category, modifier = Modifier .fillMaxWidth() .background(MaterialTheme.colorScheme.surface) .padding(16.dp), style = MaterialTheme.typography.titleMedium ) } items(categoryItems) { item -> ItemRow(item = item) } } }

// Adaptive grid LazyVerticalGrid( columns = GridCells.Adaptive(minSize = 150.dp), contentPadding = PaddingValues(16.dp), horizontalArrangement = Arrangement.spacedBy(12.dp), verticalArrangement = Arrangement.spacedBy(12.dp) ) { items(items) { item -> ItemCard(item = item) } }

  1. Navigation Patterns

Bottom Navigation:

@Composable fun MainScreen() { val navController = rememberNavController()

Scaffold(
    bottomBar = {
        NavigationBar {
            val navBackStackEntry by navController.currentBackStackEntryAsState()
            val currentDestination = navBackStackEntry?.destination

            NavigationDestination.entries.forEach { destination ->
                NavigationBarItem(
                    icon = { Icon(destination.icon, contentDescription = null) },
                    label = { Text(destination.label) },
                    selected = currentDestination?.hierarchy?.any {
                        it.route == destination.route
                    } == true,
                    onClick = {
                        navController.navigate(destination.route) {
                            popUpTo(navController.graph.findStartDestination().id) {
                                saveState = true
                            }
                            launchSingleTop = true
                            restoreState = true
                        }
                    }
                )
            }
        }
    }
) { innerPadding ->
    NavHost(
        navController = navController,
        startDestination = NavigationDestination.Home.route,
        modifier = Modifier.padding(innerPadding)
    ) {
        composable(NavigationDestination.Home.route) { HomeScreen() }
        composable(NavigationDestination.Search.route) { SearchScreen() }
        composable(NavigationDestination.Profile.route) { ProfileScreen() }
    }
}

}

Navigation Drawer:

@Composable fun DrawerNavigation() { val drawerState = rememberDrawerState(DrawerValue.Closed) val scope = rememberCoroutineScope()

ModalNavigationDrawer(
    drawerState = drawerState,
    drawerContent = {
        ModalDrawerSheet {
            Spacer(Modifier.height(12.dp))
            Text(
                "App Name",
                modifier = Modifier.padding(16.dp),
                style = MaterialTheme.typography.titleLarge
            )
            HorizontalDivider()

            NavigationDrawerItem(
                icon = { Icon(Icons.Default.Home, null) },
                label = { Text("Home") },
                selected = true,
                onClick = { scope.launch { drawerState.close() } }
            )
            NavigationDrawerItem(
                icon = { Icon(Icons.Default.Settings, null) },
                label = { Text("Settings") },
                selected = false,
                onClick = { }
            )
        }
    }
) {
    Scaffold(
        topBar = {
            TopAppBar(
                title = { Text("Home") },
                navigationIcon = {
                    IconButton(onClick = { scope.launch { drawerState.open() } }) {
                        Icon(Icons.Default.Menu, contentDescription = "Menu")
                    }
                }
            )
        }
    ) { innerPadding ->
        Content(modifier = Modifier.padding(innerPadding))
    }
}

}

  1. Material 3 Theming

Color Scheme:

// Dynamic color (Android 12+) val dynamicColorScheme = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { val context = LocalContext.current if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context) } else { if (darkTheme) DarkColorScheme else LightColorScheme }

// Custom color scheme private val LightColorScheme = lightColorScheme( primary = Color(0xFF6750A4), onPrimary = Color.White, primaryContainer = Color(0xFFEADDFF), onPrimaryContainer = Color(0xFF21005D), secondary = Color(0xFF625B71), onSecondary = Color.White, tertiary = Color(0xFF7D5260), onTertiary = Color.White, surface = Color(0xFFFFFBFE), onSurface = Color(0xFF1C1B1F), )

Typography:

val AppTypography = Typography( displayLarge = TextStyle( fontFamily = FontFamily.Default, fontWeight = FontWeight.Normal, fontSize = 57.sp, lineHeight = 64.sp ), headlineMedium = TextStyle( fontFamily = FontFamily.Default, fontWeight = FontWeight.Normal, fontSize = 28.sp, lineHeight = 36.sp ), titleLarge = TextStyle( fontFamily = FontFamily.Default, fontWeight = FontWeight.Normal, fontSize = 22.sp, lineHeight = 28.sp ), bodyLarge = TextStyle( fontFamily = FontFamily.Default, fontWeight = FontWeight.Normal, fontSize = 16.sp, lineHeight = 24.sp ), labelMedium = TextStyle( fontFamily = FontFamily.Default, fontWeight = FontWeight.Medium, fontSize = 12.sp, lineHeight = 16.sp ) )

  1. Component Examples

Cards:

@Composable fun FeatureCard( title: String, description: String, imageUrl: String, onClick: () -> Unit ) { Card( onClick = onClick, modifier = Modifier.fillMaxWidth(), shape = RoundedCornerShape(16.dp), colors = CardDefaults.cardColors( containerColor = MaterialTheme.colorScheme.surfaceVariant ) ) { Column { AsyncImage( model = imageUrl, contentDescription = null, modifier = Modifier .fillMaxWidth() .height(180.dp), contentScale = ContentScale.Crop ) Column(modifier = Modifier.padding(16.dp)) { Text( text = title, style = MaterialTheme.typography.titleMedium ) Spacer(modifier = Modifier.height(8.dp)) Text( text = description, style = MaterialTheme.typography.bodyMedium, color = MaterialTheme.colorScheme.onSurfaceVariant ) } } } }

Buttons:

// Filled button (primary action) Button(onClick = { }) { Text("Continue") }

// Filled tonal button (secondary action) FilledTonalButton(onClick = { }) { Icon(Icons.Default.Add, null) Spacer(Modifier.width(8.dp)) Text("Add Item") }

// Outlined button OutlinedButton(onClick = { }) { Text("Cancel") }

// Text button TextButton(onClick = { }) { Text("Learn More") }

// FAB FloatingActionButton( onClick = { }, containerColor = MaterialTheme.colorScheme.primaryContainer, contentColor = MaterialTheme.colorScheme.onPrimaryContainer ) { Icon(Icons.Default.Add, contentDescription = "Add") }

Quick Start Component

@Composable fun ItemListCard( item: Item, onItemClick: () -> Unit, modifier: Modifier = Modifier ) { Card( onClick = onItemClick, modifier = modifier.fillMaxWidth(), shape = RoundedCornerShape(12.dp) ) { Row( modifier = Modifier .padding(16.dp) .fillMaxWidth(), verticalAlignment = Alignment.CenterVertically ) { Box( modifier = Modifier .size(48.dp) .clip(CircleShape) .background(MaterialTheme.colorScheme.primaryContainer), contentAlignment = Alignment.Center ) { Icon( imageVector = Icons.Default.Star, contentDescription = null, tint = MaterialTheme.colorScheme.onPrimaryContainer ) }

        Spacer(modifier = Modifier.width(16.dp))

        Column(modifier = Modifier.weight(1f)) {
            Text(
                text = item.title,
                style = MaterialTheme.typography.titleMedium
            )
            Text(
                text = item.subtitle,
                style = MaterialTheme.typography.bodyMedium,
                color = MaterialTheme.colorScheme.onSurfaceVariant
            )
        }

        Icon(
            imageVector = Icons.Default.ChevronRight,
            contentDescription = null,
            tint = MaterialTheme.colorScheme.onSurfaceVariant
        )
    }
}

}

Best Practices

  • Use Material Theme: Access colors via MaterialTheme.colorScheme for automatic dark mode support

  • Support Dynamic Color: Enable dynamic color on Android 12+ for personalization

  • Adaptive Layouts: Use WindowSizeClass for responsive designs

  • Content Descriptions: Add contentDescription to all interactive elements

  • Touch Targets: Minimum 48dp touch targets for accessibility

  • State Hoisting: Hoist state to make components reusable and testable

  • Remember Properly: Use remember and rememberSaveable appropriately

  • Preview Annotations: Add @Preview with different configurations

Common Issues

  • Recomposition Issues: Avoid passing unstable lambdas; use remember

  • State Loss: Use rememberSaveable for configuration changes

  • Performance: Use LazyColumn instead of Column for long lists

  • Theme Leaks: Ensure MaterialTheme wraps all composables

  • Navigation Crashes: Handle back press and deep links properly

  • Memory Leaks: Cancel coroutines in DisposableEffect

Resources

  • Material Design 3

  • Jetpack Compose Documentation

  • Compose Samples

  • Material 3 Compose

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Coding

postgresql-table-design

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

nx-workspace-patterns

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

sast-configuration

No summary provided by upstream source.

Repository SourceNeeds Review