Best Tutorial for Android Jetpack Compose

Android App Development

Stay ahead with the latest tools, trends, and best practices in Android development

Expressive Material Design Components

Expressive Material Design Components - Coding Bihar
Expressive Material Design Components

Exploring the New Expressive Components in Material Design

Google has been shaping the language of digital design for nearly a decade. From the flat elegance of Material Design 1 to the adaptable Material You, each iteration has brought clarity and beauty to Android apps. With Material 3 Expressive, the next chapter unfolds — one that doesn’t just aim for consistency, but for vibrancy, emotion, and a sense of play.

Why “Expressive”?

The word expressive is intentional. This evolution of Material Design is about giving apps more personality. Instead of limiting designers to flat surfaces and strict grids, Expressive embraces color, motion, typography, and shape as tools to build deeper connections with users. In short, it’s about designing for delight, not just utility.


🔹 Button Groups

Buttons are the backbone of interactivity. But in many apps, actions live scattered across screens. Enter theButton Group, one of the newest Expressive components.

  • Customizable Sizes: From XS to XL, buttons can match the tone of the screen they’re in.
  • Shapes & Motion: Rounded corners, pill forms, and subtle animated feedback make them fun.
  • Organized Actions: Grouping related buttons improves clarity and user flow.

Use Case: In an e‑commerce app, a button group could hold “Add to Cart,” “Buy Now,” and “Wishlist,” giving users multiple pathways without cluttering the screen.

Jetpack Compose Example

Button Groups Screenshot

@Composable
fun ActionButtonGroup() {

    Box(
        Modifier.fillMaxSize(),
        contentAlignment = Alignment.Center
    ) {
        Row(
            horizontalArrangement = Arrangement.spacedBy(8.dp)
        ) {

            Button(onClick = { /* TODO */ }
            ) { Text("Add to Cart") }

            OutlinedButton(onClick = { /* TODO */ }
            ) { Text("Wishlist") }

            Button(
                onClick = { /* TODO */ },
                colors = ButtonDefaults.buttonColors(containerColor = Color.Green)
            ) {

                Text("Buy Now")

            }

        }
    }
}

🔹 FAB Menu

Floating Action Buttons (FABs) have been iconic in Material Design since the beginning. With Expressive, the familiar FAB evolves into a FAB Menu — a more powerful, colorful, and context‑aware way to surface quick actions.

  • No More Speed Dial: Instead of mini FABs, actions expand into a clear, menu‑like panel.
  • Dynamic Colors: FAB Menus inherit Material You palettes, adapting to wallpapers or themes.
  • Fluid Motion: Transitions feel organic — opening the menu feels like unfolding, not popping.

Use Case: In a note‑taking app, a FAB Menu could reveal actions like “New Note,” “Voice Memo,” and “Scan Document,” each styled with expressive color accents.

Jetpack Compose Example

FAB Menu Screenshot 1FAB Menu Screenshot 2

@Composable
fun FabMenuDemo() {
    var expanded by remember { mutableStateOf(false) }

    Box(
        modifier = Modifier.padding(16.dp)
            .fillMaxSize()
            .systemBarsPadding(),
        contentAlignment = Alignment.BottomEnd
    ) {
        Column(horizontalAlignment = Alignment.End) {
            // Show action buttons only when expanded
            if (expanded) {
                ExtendedFloatingActionButton(
                    onClick = { expanded = false }
                ) {
                    Text("New Note")
                }

                Spacer(modifier = Modifier.height(8.dp))

                ExtendedFloatingActionButton(
                    onClick = { expanded = false }
                ) {
                    Text("Voice Memo")
                }

                Spacer(modifier = Modifier.height(8.dp))

                ExtendedFloatingActionButton(
                    onClick = { expanded = false }
                ) {
                    Text("Scan Document")
                }

                Spacer(modifier = Modifier.height(16.dp))
            }

            // Main toggle FAB (always visible)
            FloatingActionButton(onClick = { expanded = !expanded }) {
                Icon(
                    imageVector = if (expanded) Icons.Default.Close else Icons.Default.Add,
                    contentDescription = "Menu"
                )
            }
        }
    }
}

M3-Expressive style Loading Indicator: one that feels vibrant, fluid, and friendly (think Material 3 Expressive: richer color, soft motion, subtle scale/pulse). Below I’ll explain the design ideas, accessibility considerations, and give a production-ready Jetpack Compose implementation you can drop into your app and customize.

Design summary — what makes an “Expressive” loading indicator

  • Colorful & adaptive: uses Material You / MaterialTheme colors (surface/primary/secondary) or dynamic palettes.
  • Fluid motion: smooth easing, easing-in/out plus subtle scale/pulse rather than harsh spinning.
  • Layered depth: describes the visual style (multiple layers rather than flat).
  • Minimal & accessible: doesn’t rely only on motion — provides content descriptions and optional progress text.
  • Configurable: size, stroke/thickness, color palette, and animation behaviors should be easy to change.

Jetpack Compose — expressive loading indicator (Kotlin)

  • This composable implements:
  • a rotating arc (indeterminate) with easing,
  • an inner pulsing circle for depth,
  • uses MaterialTheme colors (so it follows M3 dynamic palettes),
  • accessibility content description and optional label.
@Composable
fun ExpressiveLoadingIndicator() {
    val size = 56.dp
    val strokeWidth = 4.dp
    val primaryColor = MaterialTheme.colorScheme.primary
    val accentColor = MaterialTheme.colorScheme.secondary
    val glowColor = primaryColor.copy(alpha = 0.14f)

    val density = LocalDensity.current
    val strokePx = with(density) { strokeWidth.toPx() }

    // Infinite rotation
    val infiniteTransition = rememberInfiniteTransition(label = "loaderTransition")
    val rotation by infiniteTransition.animateFloat(
        initialValue = 0f,
        targetValue = 360f,
        animationSpec = infiniteRepeatable(
            animation = tween(durationMillis = 1600, easing = LinearOutSlowInEasing),
            repeatMode = RepeatMode.Restart
        ), label = "rotation"
    )

    // Sweep progress
    val sweepProgress by infiniteTransition.animateFloat(
        initialValue = 0.25f,
        targetValue = 0.85f,
        animationSpec = infiniteRepeatable(
            animation = tween(durationMillis = 900, easing = FastOutSlowInEasing),
            repeatMode = RepeatMode.Reverse
        ), label = "sweep"
    )

    // Inner pulse
    val pulse by infiniteTransition.animateFloat(
        initialValue = 0.85f,
        targetValue = 1.12f,
        animationSpec = infiniteRepeatable(
            animation = tween(durationMillis = 700, easing = EaseInOutCubic),
            repeatMode = RepeatMode.Reverse
        ), label = "pulse"
    )

    Box(
        modifier = Modifier.size(size),
        contentAlignment = Alignment.Center
    ) {
        // Glow layer
        Canvas(modifier = Modifier.fillMaxSize()) {
            val w = size.toPx()
            val center = Offset(w / 2f, w / 2f)
            val radius = (w / 2f) - strokePx * 1.5f

            drawCircle(
                brush = Brush.radialGradient(
                    colors = listOf(glowColor, Color.Transparent),
                    center = center,
                    radius = radius * 1.15f
                ),
                radius = radius * 1.15f,
                center = center,
                alpha = 1f
            )
        }

        // Rotating arc + pulsing dot
        Canvas(
            modifier = Modifier
                .fillMaxSize()
                .padding((strokeWidth * 1.2f))
        ) {
            val w = size.toPx()
            val center = Offset(w / 2f, w / 2f)
            val radius = (w / 2f) - strokePx

            val sweepAngle = (sweepProgress * 360f).coerceIn(8f, 320f)
            val startAngle = rotation

            val stroke = Stroke(width = strokePx, cap = StrokeCap.Round)

            // Background track
            drawArc(
                color = primaryColor.copy(alpha = 0.11f),
                startAngle = 0f,
                sweepAngle = 360f,
                useCenter = false,
                topLeft = Offset(center.x - radius, center.y - radius),
                size = Size(radius * 2f, radius * 2f),
                style = stroke
            )

            // Gradient arc
            val arcBrush = Brush.sweepGradient(listOf(primaryColor, accentColor, primaryColor))

            drawArc(
                brush = arcBrush,
                startAngle = startAngle,
                sweepAngle = sweepAngle,
                useCenter = false,
                topLeft = Offset(center.x - radius, center.y - radius),
                size = Size(radius * 2f, radius * 2f),
                style = stroke
            )

            // Pulsing dot
            val dotRadius = radius * 0.16f * pulse
            drawCircle(
                color = accentColor,
                radius = dotRadius,
                center = Offset(
                    center.x + radius * 0.5f * cos(Math.toRadians(rotation.toDouble())).toFloat(),
                    center.y + radius * 0.5f * sin(Math.toRadians(rotation.toDouble())).toFloat()
                ),
                alpha = 0.95f
            )
        }
    }
}

How to use

@Composable
fun LoadingScreen() {
    Box(
        modifier = Modifier.fillMaxSize(),
        contentAlignment = Alignment.Center
    ) {
        ExpressiveLoadingIndicator()
    }
}

OUTPUT:

M3-Expressive style Loading Indicator Screenshot

Customization tips

Colors: use MaterialTheme.colorScheme values so the indicator follows dynamic themes. For an even more expressive look, pick complementary accent colors.
Motion timing → a design principle from Material guidelines, describing how animations feel (speed, rhythm). Expressive tends to favor slightly slower, springy movement.

Accessibility:

  • Always provide contentDescription for screen readers.
  • Avoid long, continuous motion for users who have motion sensitivity — respect the system setting AnimationScale if needed (you can check AnimationScale through Settings.Global or use LocalView accessibility flags).
  • Provide alternative textual status where appropriate (e.g., “Loading content…”).
Progress state: if you have an actual progress value, prefer a determinate variant (progress arc that fills) for better UX.

When to prefer this expressive indicator

  • Splash screens, sync operations, background tasks where you want to add a little personality.
  • Feature discovery or onboarding flows where a friendly motion increases delight.
  • Not recommended for constantly visible micro-loaders in dense lists — where a compact, minimal spinner is better.

Beyond Components – The Expressive Philosophy

Expressive is more than just new UI widgets. It’s a philosophy that reshapes the foundation of Material Design:

  • Vibrant Colors: Palettes now feel richer, going beyond pastel tones.
  • Fluid Motion: Micro‑animations are bouncy, natural, and guide user attention.
  • Adaptive Typography: Variable fonts like Roboto Flex make text adapt to screens seamlessly.
  • Shapes & Containment: Bold containers emphasize hierarchy, helping users focus.

Expressive design is based on extensive research across thousands of users, ensuring accessibility remains a core priority alongside aesthetics.


Developer Takeaways

If you’re building with Jetpack Compose or Material 3 libraries, here’s what to know:

  1. Figma kits already support Expressive styles for faster prototyping.
  2. Compose Material APIs are gradually rolling out support for Button Groups and FAB Menus.
  3. Colors, motion specs, and typography updates can be applied globally via MaterialTheme.
  4. Start small — try replacing your FAB with a FAB Menu or grouping buttons in key flows.

Final Thoughts

Material Design has always been about marrying function with form. With Expressive, Google takes a bold step toward designs that feel human, joyful, and alive. Whether you’re refreshing an existing app or launching a new one, these components offer an opportunity to create experiences that connect emotionally with users.

The best part? You don’t have to wait — Expressive Material is rolling into Android 16 and Jetpack Compose, making it easier than ever to bring these vibrant experiences into your app.

Special Message

Welcome to Coding