Aju
Aju

Reputation: 4599

Shimmer animation not working on Jetpack Compose Android

I have created a Shimmer animation loader using Jetpack compose, but the animation is not working it's just showing the gradient.

Expected Shimmer Animation

enter image description here

Output from my code (not showing animation, it just shows gradient)

enter image description here

Here is the code for shimmer animation

fun Modifier.shimmerEffect(): Modifier = composed {
  var size by remember {
      mutableStateOf(IntSize.Zero)
  }
  val transition = rememberInfiniteTransition()
  val startOffsetX by transition.animateFloat(
    initialValue = 0f,
    targetValue = 2 * size.width.toFloat(),
    animationSpec = infiniteRepeatable(
        animation = tween(1000)
    )
  )

  background(
    brush = Brush.linearGradient(
        colors = listOf(
            Color(0xFFB5B5B5),
            Color(0xFFEBEBEB),
            Color(0xFFB5B5B5)
        ),
        start = Offset(startOffsetX, 0f),
        end = Offset(startOffsetX + size.width.toFloat(), size.height.toFloat())
    )
  ).onGloballyPositioned {
        size = it.size
    }
}

Here is the Composable function where shimmerEffect is applied

@Composable
fun ShimmerItem(modifier: Modifier = Modifier){
   Row(modifier = modifier) {
      Box(
        modifier = Modifier
            .size(100.dp)
            .clip(CircleShape)
            .shimmerEffect()
      )
   }
}

Here is the code where the ShimmerItem is being used.

@Composable
fun HomeScreen(homeViewModel: HomeViewModel = koinViewModel()) {
    when (val state = 
           homeViewModel.screenState.collectAsState().value) {
        is HomeUiState.Success -> {
             HomeLayout(data = state.homeData)
        }
        is HomeUiState.Loading -> {
             ShimmerItem()
        }
        is HomeUiState.Error -> {}
    }
}

Here is the viewModel code

private val _screenState = MutableStateFlow<HomeUiState>(HomeUiState.Loading)
val screenState: StateFlow<HomeUiState> = _screenState

init {
   fetchHomeData()
}

private fun fetchHomeData() {
   _screenState.value = HomeUiState.Loading
   viewModelScope.launch {
       when (val homeData = homeUseCase.execute()) {
          is SimpleResult.Success -> {
              _screenState.value = HomeUiState.Success(homeData.value)
          }
          is SimpleResult.Error -> {
              _screenState.value = HomeUiState.Error(homeData.error)
          }
       }
    }
}

I am not able to figure out why it's not working.

Upvotes: 0

Views: 697

Answers (2)

Philvin Eldho Joby
Philvin Eldho Joby

Reputation: 1

This code creates a global shimmer brush and can be reused

    @Composable
    fun ShimmerBrush(brushOut: @Composable (brush: Brush) -> Unit) {
        val shimmerColors = listOf(
            colorResource(id = R.color.grey_F1F5F8),
            colorResource(id = R.color.grey_BBBBBB),
            colorResource(id = R.color.grey_F1F5F8),
        )

        val transition = rememberInfiniteTransition(label = "")
        val translateAnim = transition.animateFloat(
            initialValue = 0f,
            targetValue = 1000f,
            animationSpec = infiniteRepeatable(
                animation = tween(
                    durationMillis = 1000,
                    easing = FastOutSlowInEasing
                ),
                repeatMode = RepeatMode.Reverse
            ), label = ""
        )

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

and add the brush to Spacers

                    ShimmerBrush { brush ->
                        Spacer(
                            modifier = Modifier
                                .size(40.dp)
                                .clip(CircleShape)
                                .background(brush)
                        )
                        Spacer(
                            modifier = Modifier.width(11.dp)
                        )
                        Box(modifier = Modifier.weight(5f)) {
                            Spacer(
                                modifier = Modifier
                                    .height(19.dp)
                                    .width(120.dp)
                                    .clip(RoundedCornerShape(5.dp))
                                    .background(brush)
                            )
                        }
                    }

Upvotes: 0

Megh Lath
Megh Lath

Reputation: 2214

Thanks for viewmodel code. As i said earlier that there is some ongoing task on MainThread!

You are executing homeUseCase on MainThread. Just need to use separate thread for executing homeUseCase and then animation will work fine.

Updated ViewModel code:

private fun fetchHomeData() {
   _screenState.value = HomeUiState.Loading
   viewModelScope.launch {
       withContext(Dispatchers.IO){
           when (val homeData = homeUseCase.execute()) {
              is SimpleResult.Success -> {
                  _screenState.value = HomeUiState.Success(homeData.value)
              }
              is SimpleResult.Error -> {
                  _screenState.value = HomeUiState.Error(homeData.error)
              }
           }
       }
    }
}

Explanation:

When animations or UI rendering tasks are performed, they usually occur on the main thread, which is also known as the UI thread.

The main thread is responsible for handling UI rendering, user interactions, and animations, so any long-running or blocking operations executed on the main thread can lead to poor app performance, unresponsive UI, and animations that appear choppy or not working as expected.

Upvotes: 2

Related Questions