Shimmer In Jetpack Compose

Introduction

User engagement is essential in every application. When loading data, it is crucial to display a layout or graphics that users find useful, or that indicates something is loading. The Shimmer layout comes into play at that point.

As of now, Jetpack Compose does not have an official shimmer layout. However, I am going to create an extension function of the Modifier in Compose, which can be used on any composable to implement the shimmer effect. Let's dive into the implementation.

Step 1

Create a new Project in JetPack Compose, and in the main activity, use the following code.

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            ShimmerLayoutExampleTheme {
                // A surface container using the 'background' color from the theme
                var isLoading by remember {
                    mutableStateOf(true)
                }
                LaunchedEffect(key1 = true) {
                    delay(5*1000)
                    isLoading = false
                }
                LazyColumn(
                    modifier = Modifier.fillMaxSize()
                ) {
                    items(20) {
                        ShimmerItem(isLoading = isLoading, modifier = Modifier
                                .fillMaxWidth()
                                .padding(16.dp))
            }
        }
    }
}

The provided code uses Jetpack Compose to create a loading placeholder with a Shimmer effect for a list of items. When the activity starts, the Shimmer effect is shown for each item in the list to indicate that data is being loaded. After a delay of 5 seconds, the Shimmer effect stops, and the actual content is displayed in the list. The Shimmer effect provides a subtle animation that helps improve the user experience by giving visual feedback during the data loading process.

Step 2

Now let's see how the ShimmerItem is defined.

@Composable
fun ShimmerItem(
    isLoading: Boolean,
    modifier: Modifier = Modifier
) {
    Row(
        modifier = Modifier
            .fillMaxWidth()
            .padding(16.dp)
    ) {
        Icon(
            painter = painterResource(id = R.drawable.acc),
            contentDescription = null,
            modifier = Modifier
                .align(Alignment.CenterVertically)
                .size(60.dp)
                .clip(CircleShape)
                .shimmerEffect(isLoading)
                .alpha(if (isLoading) 0f else 1f)
        )
        Spacer(
            modifier = Modifier
                .width(8.dp)
                .background(Color.Gray)
        )
        Column(modifier = Modifier.fillMaxHeight().align(Alignment.CenterVertically)) {
            Text(
                text = "Name- MR. Ravi Sahu",
                modifier = Modifier
                    .shimmerEffect(isLoading)
                    .alpha(if (isLoading) 0f else 1f)
            )
            Spacer(modifier = Modifier.height(10.dp))
            Text(
                text = "Age- 24 ",
                modifier = Modifier
                    .shimmerEffectNew(isLoading)
                    .alpha(if (isLoading) 0f else 1f)
            )
        }
        Spacer(modifier = Modifier.weight(1f))
        Text(
            text = "Contact here -  ",
            modifier = Modifier
                .align(Alignment.CenterVertically)
                .shimmerEffect(isLoading)
                .alpha(if (isLoading) 0f else 1f)
        )

    }
}

Here we define a custom Composable function called 'ShimmerItem', which creates a row containing multiple UI elements with a shimmer effect. This shimmer effect acts as a loading placeholder. When the isLoading variable is set to 'true', indicating data is loading, the shimmer effect is shown. Once isLoading is set to false, the shimmer effect stops, and the actual content is displayed.

The ShimmerItem composable includes an 'Icon', two Text elements within a 'Column', and a final Text element. Each of these elements has a shimmer effect applied to it. During loading (isLoading = true), the alpha (opacity) of these elements is set to 0, making them invisible and showing the shimmer effect. Once isLoading becomes false, the alpha is set to 1, revealing the actual content and stopping the shimmer effect.

The shimmer effect is achieved using a custom extension function called 'shimmerEffect', which modifies the composable to include the shimmer animation. The overall result is a visually appealing loading experience with shimmering placeholders until the actual data is ready to be displayed.

Step 3

Let's move on to the section where we define our extension function, "shimmerEffect."


fun Modifier.shimmerEffectNew(toShow:Boolean): Modifier = composed {

    if (toShow){
        var size by remember {
            mutableStateOf(IntSize.Zero)
        }
        val transition = rememberInfiniteTransition()
        val startOffsetX by transition.animateFloat(
            initialValue = -2 * size.width.toFloat(),
            targetValue = 2 * size.width.toFloat(),
            animationSpec = infiniteRepeatable(
                animation = tween(1000)
            )
        )
        background(
            brush = Brush.linearGradient(
                colors = listOf(
                    Color(0xFFB8B5B5),
                    Color(0xFF8F8B8B),
                    Color(0xFFB8B5B5),
                ),
                start = Offset(startOffsetX, 0f),
                end = Offset(startOffsetX + size.width.toFloat(), size.height.toFloat())
            )
        )
            .onGloballyPositioned {
                size = it.size
            }
    }else{
        Modifier
    }
}

Here we have an extension function shimmerEffect on the Modifier class in Jetpack Compose. This extension function is used to apply a shimmer effect to a composable element when toShow is set to true, and it returns an unmodified Modifier when toShow is false.

Here's a detailed explanation of the code:

  1. The shimmerEffect extension function takes a single parameter toShow, which is a Boolean value. This parameter indicates whether the shimmer effect should be shown (true) or not (false).

  2. Inside the function, a Composed Modifier is used to handle the shimmer effect conditionally. If toShow is true, the shimmer effect is applied; otherwise, an unmodified Modifier is returned.

  3. When toShow is true, the composed composable is used to create a custom modifier that will add the shimmer effect.

  4. Within the shimmer effect block:

    • A 'size' the variable is declared and initialized with the 'IntSize.Zero'. This variable will be used to store the size of the composable element to apply the gradient brush correctly.
    • The 'rememberInfiniteTransition' is used to create an infinite transition that animates the shimmer effect horizontally.
    • The 'startOffsetX' the the variable is animated using the 'animateFloat' function from the transition object. It sets the horizontal offset for the shimmer effect. The 'startOffsetX' starts from -2 * size.width and ends at 2 * size.width, creating a shimmering effect that moves back and forth horizontally.
    • The background 'modifier' is applied to the composable to add the shimmer effect. It uses a linear gradient brush with colors ranging from light to dark grey. The gradient brush's start and end offsets are based on the startOffsetX animation value to create the shimmering effect.
    • The onGloballyPositioned modifier is used to capture the size of the composable element once it is laid out on the screen. The size variable is updated with the computed size of the composable.
  5. Finally, if toShow is false, the function returns an 'Unmodified Modifier', which means no shimmer effect will be applied.

Output

Summary

This article explains how we can implement a shimmer layout in a list of items. Overall, the shimmerEffect extension function enables the creation of shimmering loading placeholders for Jetpack Compose composable elements. When toShow is true, the shimmer effect is displayed, giving a visually appealing loading experience. When toShow is false, the shimmer effect is removed, and the actual content is shown. Hope this has been a helpful guide for you; it will be helpful for me if you write your genuine comment about this article. Thank you!


Similar Articles