How to do the parallax scroll with jetpack compose

In this tutorial you will learn how to do the parallax effect in jetpack compose on an image in a scroll view. You will learn how to use the Column, scroll it using the verticalScroll, how to use graphicsLayer to apply the parallax effect on the image. You will also learn how to load an image asynchronously and how to place the views inside the scrollview using the Column view and jetpack compose view modifiers.

Define the view

Here is the basic view we will use. It's composed by a Column that scrolls vertically using the verticalScroll modifier, an image and a text.

val scrollState = rememberScrollState()
    Column(
        modifier = Modifier
            .fillMaxSize()
            .verticalScroll(scrollState)
    ) {
        AsyncImage(
            model = "https://images.unsplash.com/photo-1648737922331-0b5e338215bc?crop=entropy&cs=tinysrgb&fit=crop&fm=jpg&h=300&ixid=MnwxfDB8MXxyYW5kb218MHx8fHx8fHx8MTY0OTg2NTgyNA&ixlib=rb-1.2.1&q=80&w=720",
            contentDescription = null,
            contentScale = ContentScale.Crop,
            modifier = Modifier
                .fillMaxWidth()
                .height(400.dp)               
        )
        Column(modifier = Modifier
            .background(Color.White)
            .fillMaxWidth()
            .padding(10.dp)) {
            Text(
                text = stringResource(R.string.loremlipsum),
                fontSize = 15.sp,
                lineHeight = 30.sp
            )
        }

    }

The image is loaded using the coil AsyncImage component. The Column modifier allows to put views with a vertical order. The fillMaxSize allows to extend the view to the maximum available size.

Jetpack Compose Parallax Effect on scroll view

Parallax Effect with jetpack compose

In order to do the parallax effect with jetpack compose, you will need to use the graphicsLayer modifier. Here are all the transitions that can be applied to the graphicsLayer modifier:

graphicslayer modifier transition
        alpha = alpha,
        translationX = translationX,
        translationY = translationY,
        shadowElevation = shadowElevation,
        scaleX = scaleX,
        scaleY = scaleY,
        rotationX = rotationX,
        rotationY = rotationY,
        rotationZ = rotationZ,
        cameraDistance = cameraDistance,
        transformOrigin = TransformOrigin(originX, originY),
        shape = roundedDegree,
        clip = true,
        renderEffect = BlurEffect(blur, blur)

In our example, we will use the translationY modifier to move the image down, while scrolling up, and doind the parallax effect.

parallax effect
 .graphicsLayer {
                    translationY = scrollState.value * 0.2f
                }

You can also apply the blur effect while scrolling, or any other effect you wish:

blur effect with graphicsLayer
  .graphicsLayer {
                    translationY = scrollState.value * 0.2f
                    if(scrollState.value > 0){
                        renderEffect = BlurEffect( scrollState.value * 0.1f,  scrollState.value * 0.1f)
                    }
                }

Final Code

@Composable
fun View() {
    val scrollState = rememberScrollState()
    Column(
        modifier = Modifier
            .fillMaxSize()
            .verticalScroll(scrollState)
    ) {
        AsyncImage(
            model = "https://images.unsplash.com/photo-1648737922331-0b5e338215bc?crop=entropy&cs=tinysrgb&fit=crop&fm=jpg&h=300&ixid=MnwxfDB8MXxyYW5kb218MHx8fHx8fHx8MTY0OTg2NTgyNA&ixlib=rb-1.2.1&q=80&w=720",
            contentDescription = null,
            contentScale = ContentScale.Crop,
            modifier = Modifier
                .fillMaxWidth()
                .height(400.dp)
                .graphicsLayer {
                    translationY = scrollState.value * 0.2f
                    if(scrollState.value > 0){
                        renderEffect = BlurEffect( scrollState.value * 0.1f,  scrollState.value * 0.1f)
                    }
                }
        )
        Column(modifier = Modifier
            .background(Color.White)
            .fillMaxWidth()
            .padding(10.dp)) {
            Text(
                text = stringResource(R.string.loremlipsum),
                fontSize = 15.sp,
                lineHeight = 30.sp
            )
        }

    }
}

 

 

Other articles