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:
-
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
).
-
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.
-
When toShow
is true
, the composed
composable is used to create a custom modifier that will add the shimmer effect.
-
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.
-
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!