Skeleton UI in jetpack compose, the lost design

Cristian Gomez
2 min readJul 4, 2022

I will keep this story short, looking around the web I did not find any skeleton UI which follows this specific patterns, where the animation of the animated components feels like are sharing the same pattern.

FInished example

Most animations for skeleton UIs, starts on the top left and goes to the bottom right and does not feels continuous.

The code

@Composable
fun RowScope.LoadingShimmerEffect(
) {
val gradient = listOf(
themedDarkBlueSilverLight().copy(alpha = 0.9f),
themedDarkBlueSilverLight().copy(alpha = 0.3f),
themedDarkBlueSilverLight().copy(alpha = 0.9f),
themedDarkBlueSilverLight().copy(alpha = 0.9f),
themedDarkBlueSilverLight().copy(alpha = 0.9f),
)

val transition = rememberInfiniteTransition()
val translateAnim by transition.animateFloat(
initialValue = 0f,
targetValue = 1f,
animationSpec = infiniteRepeatable(tween(durationMillis = 3200, easing = LinearEasing))
)

var positionInRootTopBar by remember { mutableStateOf(Offset.Zero) }
val configuration = LocalConfiguration.current
val screenHeight = configuration.screenHeightDp.px
val screenWidth = configuration.screenWidthDp.px

val diff = screenWidth * translateAnim * 4

val initialX = diff - (positionInRootTopBar.x) - (screenWidth * 2)
val endX = (screenWidth * 2) + initialX

val initialY = -positionInRootTopBar.y + diff
val endY = screenHeight - positionInRootTopBar.y + diff
val brush = Brush.linearGradient(
colors = gradient,
start = Offset(initialX, initialY),
end = Offset(endX, endY),
)
Box(
modifier = Modifier
.padding(10.dp)
.background(brush)
.weight(1f)
.height(200.dp)
.onGloballyPositioned {
positionInRootTopBar = it.positionInRoot()
}
)
}

val Int.px: Int get() = (this * getSystem().displayMetrics.density).toInt()

I will explain all the code in a next in depth article, but so far, the idea is to use the global position to know where/when the animation starts.

Happy coding!

--

--