Shimmer/Skeleton animation in Android Jetpack Compose

How can I create an animation like this to represent objects that are loading, thanks to everyone. Shimmer example

I looked for the whole day but the information that I found was in views, if anyone knows a dependency or how to create this I will be thankful.

Upvotes: 1

Views: 1934

Answers (3)

hasan.z
hasan.z

Reputation: 339

The shimmer code works well, but it has a major recomposition issue. Here, I’ve optimized it using drawBehind to improve performance.

@Composable
fun Modifier.shimmerLoading(
    durationMillis: Int = 1000,
): Modifier {
    val transition = rememberInfiniteTransition(label = "")

    val translateAnimation by transition.animateFloat(
        initialValue = 0f,
        targetValue = 500f,
        animationSpec = infiniteRepeatable(
            animation = tween(
                durationMillis = durationMillis,
                easing = LinearEasing,
            ),
            repeatMode = RepeatMode.Restart,
        ),
        label = "",
    )

    return drawBehind {
        drawRect(
            brush = Brush.linearGradient(
                colors = persistentListOf(
                    Color.LightGray.copy(alpha = 0.2f),
                    Color.LightGray.copy(alpha = 1.0f),
                    Color.LightGray.copy(alpha = 0.2f),
                ),
                start = Offset(x = translateAnimation, y = translateAnimation),
                end = Offset(x = translateAnimation + 100f, y = translateAnimation + 100f),
            )
        )
    }
}

Using this is straightforward — just call it to the Modifier of your composable.

Box(
    modifier = Modifier
        .size(80.dp)
        .clip(CircleShape)
        .shimmerLoading()
)

I highly recommend checking out this Medium article titled: "Shimmer Animation in Jetpack Compose Without Recomposition" about creating a shimmer effect with no recomposition and optimized performance. It’s packed with great tips!

Upvotes: -1

harunkor
harunkor

Reputation: 89

Accompanist placeholder api is deprecated. You can use the following structure instead.

@Composable
fun ShimmerEffectBox(
    modifier: Modifier = Modifier, isShow: Boolean = true,
    content: @Composable BoxScope.() -> Unit
) {
    val shimmerColors = listOf(
        Color.LightGray.copy(alpha = 0.6f),
        Color.LightGray.copy(alpha = 0.2f),
        Color.LightGray.copy(alpha = 0.6f)
    )

    val transition = rememberInfiniteTransition(label = "")
    val translateAnim by transition.animateFloat(
        initialValue = 0f, targetValue = 1000f, animationSpec = infiniteRepeatable(
            animation = tween(durationMillis = 1700, delayMillis = 200),
            repeatMode = RepeatMode.Restart
        ), label = ""
    )

    val brush = Brush.linearGradient(
        colors = shimmerColors,
        start = Offset.Zero,
        end = Offset(x = translateAnim, y = translateAnim)
    )


    if (isShow) {
        Box(
            modifier = modifier.background(brush)
        ) {
            Box(modifier = Modifier
                .matchParentSize()
                .graphicsLayer { alpha = 1f })
        }
    } else {
        Box(
            modifier = modifier
        ) {
            content()
        }

    }
}

Usage:

  Row {
            ShimmerEffectBox(
                modifier = Modifier
                    .height(100.dp)
                    .padding(4.dp)
                    .weight(0.3f)
                    .clip(RoundedCornerShape(4.dp)),
                isShow = true
            ) {

                Image(painter = painterResource(id = R.drawable.ic_launcher_background), contentDescription = "")
            }
            ShimmerEffectBox(
                modifier = Modifier
                    .height(100.dp)
                    .padding(4.dp)
                    .weight(0.7f)
                    .clip(RoundedCornerShape(4.dp)),
                isShow = true
            ) {

                Text(
                    text = "Content to display after content has loaded"
                )
            }
        }

Upvotes: 0

Egor
Egor

Reputation: 13

you can use rememberInfiniteTransition, for example simple shimmer animation below

val infiniteTransition = rememberInfiniteTransition(label = "")
val skeletonAlpha by infiniteTransition.animateFloat(
    // between these states, the skeleton will flicker
    initialValue = 1f, // skeleton max alpha
    targetValue = 0.5f, // skeleton min alpha
    animationSpec = infiniteRepeatable(
    // every 500ms, but you can change this time 
        animation = tween(500, easing = LinearEasing),
        repeatMode = RepeatMode.Reverse
    ), label = ""
)
Card(modifier = Modifier.alpha(skeletonAlpha))

Upvotes: 0

Related Questions