How to animate Gradient Text colors in Jetpack Compose

Bring Your Text to Life with Gradients in Jetpack Compose

Ever stared at plain text in your app and thought, "This needs some sparkle"? Gradients are your answer! They're like blending colors smoothly - think pink fading into blue - to make text stand out and feel fun. 

In Jetpack Compose, it's super easy with a thing called Brush. We'll walk through cool ways to use them, step by step, so you can try it yourself. No fancy terms, just simple fun that makes your apps look awesome.


Quick Peek: What You'll See Here

We're using the phrase "Build better apps faster with Jetpack Compose" as our playground. Each example shows a different gradient trick, with code ready to copy. We'll explain why it works and where to use it. Let's start with the basics and build up!

1. Candy Cane Shimmer: Smooth Diagonal Glow 

Picture colors sliding diagonally like candy stripes in motion. It's animated, so the gradient keeps shifting - great for grabbing attention without being too wild.


@Composable
fun candyCaneBrush(fontSize: androidx.compose.ui.unit.TextUnit, colors: List<Color>): Brush {
    val density = LocalDensity.current
    val fontSizePx = with(density) { fontSize.toPx() }
    val infiniteTransition = rememberInfiniteTransition()
    val offset by infiniteTransition.animateFloat(
        initialValue = 0f,
        targetValue = fontSizePx * 2,
        animationSpec = infiniteRepeatable(
            animation = tween(2000),
            repeatMode = RepeatMode.Restart
        )
    )

    return Brush.linearGradient(
        colors = colors,
        start = Offset(0f, 0f),
        end = Offset(offset, offset),
        tileMode = TileMode.Mirror
    )
}

// Usage in Text:
Text(
    text = text,
    style = TextStyle(
        fontSize = fontSize,
        fontWeight = FontWeight.Bold,
        brush = candyCaneBrush(fontSize, colors)
    )
)
        

Why Try It: The slide uses a loop to move colors smoothly. Flip with TileMode.Mirror for no breaks. Perfect for app titles or buttons that need energy.


2. Back and Forth Shimmer: Gentle Wave Motion 

This one rocks the colors side to side like a calm wave. It's animated but chill, adding life to text without overwhelming your screen.


@Composable
fun backAndForthBrush(colors: List<Color>): Brush {
    val infiniteTransition = rememberInfiniteTransition()
    val offset by infiniteTransition.animateFloat(
        initialValue = 0f,
        targetValue = 1f,
        animationSpec = infiniteRepeatable(
            animation = tween(2000),
            repeatMode = RepeatMode.Reverse
        )
    )

    return remember(offset) {
        ShaderBrush(
            LinearGradientShader(
                from = Offset.Zero,
                to = Offset(offset * 1000f, offset * 1000f), // Scaled for drawing area
                colors = colors,
                tileMode = TileMode.Mirror
            )
        )
    }
}

// Usage in Text:
Text(
    text = text,
    style = TextStyle(
        fontSize = fontSize,
        fontWeight = FontWeight.Bold,
        brush = backAndForthBrush(colors)
    )
)
        

Why Try It: Bounces back with RepeatMode.Reverse. Scales to fit your text. Use for alerts or menus where subtle movement feels right.


3. Static Gradient: Simple Color Blend 

No moving parts here - just a steady fade from one color to another. Quick to set up and always looks sharp.


// Usage in Text:
Text(
    text = text,
    style = TextStyle(
        fontSize = fontSize,
        fontWeight = FontWeight.Bold,
        brush = Brush.linearGradient(colors)
    )
)
        

Why Try It: Just one line with Brush.linearGradient. No battery drain from animation. Great for logos or labels that stay put.


4. Partial Gradient: Highlight Just a Bit 

Put the gradient on only part of your text, like spotlighting a word. Keeps the rest normal for contrast.


// Usage in Text:
Text(
    text = buildAnnotatedString {
        withStyle(style = SpanStyle(brush = Brush.linearGradient(colors))) {
            append("Animating brush ")
        }
        append("Text coloring in Compose")
    },
    style = TextStyle(
        fontSize = fontSize,
        fontWeight = FontWeight.Bold
    )
)
        

Why Try It: Pick spots with buildAnnotatedString. Draws eyes to important words. Handy for tips or ads.


5. Gradient with Opacity: Soft, Faded Look 

Like the static one but toned down - colors are there, but not too bright. Gives a gentle, dreamy vibe.


// Usage in Text:
Text(
    text = text,
    style = TextStyle(
        fontSize = fontSize,
        fontWeight = FontWeight.Bold,
        brush = Brush.linearGradient(colors),
        alpha = 0.5f
    )
)
        

Why Try It: Fade with alpha = 0.5f. Keeps things light. Good for hints or backgrounds.


Which One's Best? A Quick Comparison

Here's how they stack up - ranked by cool factor, ease, and when to use them.

Rank Effect Pros Cons
1 Candy Cane Shimmer Fun animation; grabs eyes fast Uses a bit more power
2 Back and Forth Shimmer Smooth wave; feels alive Adjust for big text
3 Partial Gradient Spotlights words; flexible Extra code steps
4 Static Gradient Easy and bright No motion
5 Gradient with Opacity Soft and classy Might look too light

Tips to Get Started Right

  • Pick Colors Wisely: 2-3 max, like pink (#FC5C7D) to blue (#6A82FB) for a cool vibe.
  • Loop Animations: Use rememberInfiniteTransition to keep things running smooth.
  • Dark/Light Mode: Check with isSystemInDarkTheme() so text always shows up clear.
  • Test It Out: Run on real phones to see speed.
  • Level Up: Add shadows or lines for more pop.

Common Questions Answered

What's the Easiest Gradient to Start With?

Just add Brush.linearGradient to your text style - done!


How Do I Make Loops Without Glitches?

Go with infiniteRepeatable and tween for easy flow.


Where to Find More Color Ideas?

Check this spot: "Collection of 50 handpicked gradients" - full of ready-to-use combos.


Can I Use These on Buttons Too?

Yes! Anything that takes a Brush works - get creative.

Full Code to Copy and Run

Here's everything in one go. Paste into your project and see the magic happen!


package com.android.uix

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.compose.animation.core.RepeatMode
import androidx.compose.animation.core.animateFloat
import androidx.compose.animation.core.infiniteRepeatable
import androidx.compose.animation.core.rememberInfiniteTransition
import androidx.compose.animation.core.tween
import androidx.compose.foundation.background
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.Divider
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.darkColorScheme
import androidx.compose.material3.lightColorScheme
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.LinearGradientShader
import androidx.compose.ui.graphics.ShaderBrush
import androidx.compose.ui.graphics.TileMode
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.withStyle
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp

class MainActivity3 : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        enableEdgeToEdge() // Enables edge-to-edge display for immersive UI
        setContent {
            val isDarkTheme = isSystemInDarkTheme() // Detect system dark mode
            MaterialTheme(
                colorScheme = if (isDarkTheme) darkColorScheme(
                    background = Color.Black, // Pure black background in dark mode
                    onBackground = Color.White // White text/icons on black
                ) else lightColorScheme(
                    background = Color.White, // Pure white background in light mode
                    onBackground = Color.Black // Black text/icons on white
                )
            ) {
                GradientTextDemo()
            }
        }
    }
}

@Composable
fun GradientTextDemo() {
    val text = "Build better apps faster with\n" +
            "Jetpack Compose"
    val fontSize = 30.sp
    val colors = listOf(Color(0xFFFC5C7D), Color(0xFF6A82FB)) // Gradient colors for text: #FC5C7D to #6A82FB

    Column(
        modifier = Modifier
            .fillMaxSize()
            .background(MaterialTheme.colorScheme.background) // Explicitly set background to theme's background color
            .padding(16.dp)
            .verticalScroll(rememberScrollState()),
        horizontalAlignment = Alignment.CenterHorizontally,
        verticalArrangement = Arrangement.Top
    ) {

        Spacer(Modifier.height(10.dp))
        Text(
            text = "Animating brush Text coloring in Compose",
        )

        // Candy Cane Shimmer
        Text(
            text = text,

            style = TextStyle(
                fontSize = fontSize,
                fontWeight = FontWeight.Bold,
                brush = candyCaneBrush(fontSize, colors)
            )
        )
        Text(
            text = "Candy Cane Shimmer: Diagonal moving gradient for a candy-like effect, feel the smooth animation flow",
            style = TextStyle(fontSize = 14.sp, color = MaterialTheme.colorScheme.onBackground.copy(alpha = 0.6f)) // Adaptive caption color for visibility in dark/light
        )
        Divider(modifier = Modifier.padding(vertical = 16.dp), color = MaterialTheme.colorScheme.onBackground.copy(alpha = 0.3f)) // Adaptive divider color

        // Back and Forth Shimmer
        Text(
            text = text,
            style = TextStyle(
                fontSize = fontSize,
                fontWeight = FontWeight.Bold,
                brush = backAndForthBrush(colors)
            )
        )
        Text(
            text = "Back and Forth Shimmer: Gradient rocks side to side, creating a dynamic waving motion",
            style = TextStyle(fontSize = 14.sp, color = MaterialTheme.colorScheme.onBackground.copy(alpha = 0.6f)) // Adaptive caption color for visibility in dark/light
        )
        Divider(modifier = Modifier.padding(vertical = 16.dp), color = MaterialTheme.colorScheme.onBackground.copy(alpha = 0.3f)) // Adaptive divider color

        // Static Gradient
        Text(
            text = text,
            style = TextStyle(
                fontSize = fontSize,
                fontWeight = FontWeight.Bold,
                brush = Brush.linearGradient(colors)
            )
        )
        Text(
            text = "Static Gradient: Fixed color blend from pink to blue, no animation but vibrant look",
            style = TextStyle(fontSize = 14.sp, color = MaterialTheme.colorScheme.onBackground.copy(alpha = 0.6f)) // Adaptive caption color for visibility in dark/light
        )
        Divider(modifier = Modifier.padding(vertical = 16.dp), color = MaterialTheme.colorScheme.onBackground.copy(alpha = 0.3f)) // Adaptive divider color

        // Partial Gradient
        Text(
            text = buildAnnotatedString {
                withStyle(style = SpanStyle(brush = Brush.linearGradient(colors))) {
                    append("Animating brush ")
                }
                append("Text coloring in Compose")
            },
            style = TextStyle(
                fontSize = fontSize,
                fontWeight = FontWeight.Bold
            )
        )
        Text(
            text = "Partial Gradient: Gradient on 'Animating brush' only, highlights key phrase for emphasis",
            style = TextStyle(fontSize = 14.sp, color = MaterialTheme.colorScheme.onBackground.copy(alpha = 0.6f)) // Adaptive caption color for visibility in dark/light
        )
        Divider(modifier = Modifier.padding(vertical = 16.dp), color = MaterialTheme.colorScheme.onBackground.copy(alpha = 0.3f)) // Adaptive divider color

        // Gradient with Opacity
        Text(
            text = text,
            style = TextStyle(
                fontSize = fontSize,
                fontWeight = FontWeight.Bold,
                brush = Brush.linearGradient(colors),
                alpha = 0.5f
            )
        )
        Text(
            text = "Gradient with Opacity: Faded gradient effect, subtle and elegant for softer visuals",
            style = TextStyle(fontSize = 14.sp, color = MaterialTheme.colorScheme.onBackground.copy(alpha = 0.6f)) // Adaptive caption color for visibility in dark/light
        )
    }
}

@Composable
fun candyCaneBrush(fontSize: androidx.compose.ui.unit.TextUnit, colors: List<Color>): Brush {
    val density = LocalDensity.current
    val fontSizePx = with(density) { fontSize.toPx() }
    val infiniteTransition = rememberInfiniteTransition()
    val offset by infiniteTransition.animateFloat(
        initialValue = 0f,
        targetValue = fontSizePx * 2,
        animationSpec = infiniteRepeatable(
            animation = tween(2000),
            repeatMode = RepeatMode.Restart
        )
    )

    return Brush.linearGradient(
        colors = colors,
        start = Offset(0f, 0f),
        end = Offset(offset, offset),
        tileMode = TileMode.Mirror
    )
}

@Composable
fun backAndForthBrush(colors: List<Color>): Brush {
    val infiniteTransition = rememberInfiniteTransition()
    val offset by infiniteTransition.animateFloat(
        initialValue = 0f,
        targetValue = 1f,
        animationSpec = infiniteRepeatable(
            animation = tween(2000),
            repeatMode = RepeatMode.Reverse
        )
    )

    return remember(offset) {
        ShaderBrush(
            LinearGradientShader(
                from = Offset.Zero,
                to = Offset(offset * 1000f, offset * 1000f), // Scaled for drawing area
                colors = colors,
                tileMode = TileMode.Mirror
            )
        )
    }
}
        

That's it - now go add some color to your apps!






Post a Comment

Previous Post Next Post