Jetpack Compose is the new and trending way to build Android UI.
At BAM, we use it on all new projects. On older projects, we make a plan to refactor good old XML to Composable.
This is the first post of a series of How to do this with Jetpack Compose.
Posts in this series :
Resources:
Let's go !
Do you know Instagram stories ? They are a slideshow of full screen mobile pictures. Here is an example :
Instagram story (from @themonkeyuser)
We will try to recode the progress bar like this :
Our Jetpack Compose Stories !
Specs are :
We will use a ++code>Row++/code> with a background for the progress bar background.
And a ++code>Box++/code> for the progress bar foreground.
++pre>++code class="has-line-data" data-line-start="32" data-line-end="50">@Preview(widthDp = 200)
@Composable
fun ProgressBarPreview() {
Row(
modifier = Modifier
.height(4.dp)
.clip(RoundedCornerShape(50, 50, 50, 50)) // (1)
.background(Color.White.copy(alpha = 0.4f)) // (2)
) {
Box(
modifier = Modifier
.background(Color.White)
.fillMaxHeight()
.fillMaxWidth(0.5f), // (3)
) {}
}
}
++/code>++/pre>
Result :
We need to iterate over an index.
++pre>++code class="has-line-data" data-line-start="64" data-line-end="101">@Preview(widthDp = 600)
@Composable
fun InstagramSlicedProgressBar(
steps: Int = 3,
currentStep: Int = 2
) {
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.height(48.dp)
.padding(24.dp, 0.dp),
) {
for (index in 1..steps) { // (1)
// We use our previous code :
Row(
...
) {
Box(
modifier = Modifier
...
.fillMaxHeight().let {
// Here we check if we need to fill the progress bar of not :
when (index) { // (2)
currentStep -> it.fillMaxWidth(.5f)
in 0..currentStep -> it.fillMaxWidth(1f)
else -> it
}
},
) {}
}
if (index != steps) {
Spacer(modifier = Modifier.width(4.dp)) // (3)
}
}
}
}
++/code>++/pre>
Result :
We will need a ++code>LaunchedEffect++/code> :
++pre>++code class="has-line-data" data-line-start="116" data-line-end="157">@Preview(widthDp = 600)
@Composable
fun InstagramSlicedProgressBar(
steps: Int = 3,
currentStep: Int = 2,
paused: Boolean = false,
onFinished: () -> Unit = {}
) {
val percent = remember { Animatable(0f) } // (1)
LaunchedEffect(paused) { // (2)
if (paused) percent.stop()
else {
percent.animateTo(
targetValue = 1f,
animationSpec = tween(
durationMillis = (5000 * (1f - percent.value)).toInt(), // (3)
easing = LinearEasing
)
)
onFinished() // (4)
}
}
Row(...) {
for (...) {
Row(...) {
Box(
modifier = Modifier
...
.fillMaxHeight().let {
when (index) {
currentStep -> it.fillMaxWidth(percent.value) // (5)
...
}
},
) {}
}
...
}
}
}
++/code>++/pre>
Tadaaa ?
We are done with our progress bar !