Parallax effect in PageView – UI Tickets Challenge

Parallax effect in PageView – UI Tickets Challenge

Parallax effects are awesome. Having elements move in the different speed during scrolling can easily provide the unique feeling for the application and they can make the user think that your app is well-polished. In this post, I will try to achieve parallax effect using PageView, Transforms, Alignments and some basic math. To show you how to do that, I picked the Buy Tickets design from dribbble created by Dldp. Let’s do it! 🙂

The original design

This post focuses only on the slider part of the design. We will cover creating the cards, the slider and then we will move images, texts and buttons inside those cards.
The bottom sheet is described in this post. 🙂

Starting point

I don’t want to waste our time on the aspects that don’t really matter, so let’s skip the headers and bottom menu and focus only on the slider. You can always find the full code in here. This is our starting point:

We have used the Placeholder widget in place of future PageView and it looks like this:

Starting point

The slider

Creating the cards

Let’s replace that placeholder with some actual content. We will create a new widget called SlidingCard which will represent one element in the slider. So far let’s wrap it in SizedBox. We will introduce PageView later on. So the _SlidingCardsViewState now  looks like this:

Now, what should SlidingCard look like? It definitely drops some shadows so we should think of Material or Card widget. I went with the Card. We can see that this card needs to have rounded corners, so will need to specify shape parameter. We can also notice that the image will cover slightly over half of the card. We will add that too. Let’s look at the code:

As said earlier, we used Card. We have specified the elevation to 8 to have a bigger shadow. Using RoundedRectangleBorder as card’s shape let us round down the corners, however, the image displayed on the top wouldn’t respect that and it would have sharp corners sticking out of the card. To avoid that we can use the ClipRRect widget to simply clip the image to match the card’s shape. At this moment the app looks like this:

Now we can add the content of the card. It is rather simple: a Column with Texts and Row with more Texts:

The whole card should look like that:

You may notice that the fonts don’t exactly match but let it be our little secret. 😉

Creating the PageView

Now when we have the SlidingCard widget we can focus on the PageView. Adding a PageView is very simple. However, in our design you can see more than one card at the time, there is also a small part of the next card. To achieve that, we will use PageController with viewportFraction parameter, which determines how much space of the main axis should the one page take. In our case, it will be 80%.

Cards in PageView

The parallax effect


It’s time for the parallax effect! I want to keep it as simple as possible, so we will avoid adding any complex animations and focus only on what we get from PageController. And what we get is page, which gives the information on which page is currently displayed. It is a double and it works like this: 0 means the first page, 1 means the second page and let’s say 0.3 means 30% way between the first and the second page. Having that, we can pass to each card the page - cardIndex which will basically mean: how far is this card from being displayed.

It’s worth mentioning that the approach with the setState means that everytime pageController emits value, the whole _SlidingCardsViewState will be rebuilt. If you want to make it more efficient, you can wrap every widget that would depend on the pageOffset in AnimatedBuilder. AnimatedBuilder can accept the scrollable (pageController), the child and the builder and it would rebuild only the widgets that need rebuilding. Despite that I will stay with setState to keep the code as simple as possible but remember that this is not the optimal way to do it.
Thanks to Simon Lightfoot for pointing out this issue.

Having the information on “how far is the page from being in the displayed” named as offset we can have some fun with it. When it comes to images, the easiest way is to change the alignment of the image. Having alignment depend on the offset, we will make images see move parallel to the movement of the card.

I have used -offset.abs() to get the look from the design but you can experiment with that value to find the one that suits your app best.

Now we can see that images look like they’re moving in a slightly different speed than the cards:

Parallax images


Now before we move the texts and buttons, we need to notice that in the design those elements are always moving in the same direction and that they need to come back to their original place after the movement. That means, we cannot use a linear approach towards the offset – we will use the Gaussian function. For those, who don’t know what it is, let me show you a simple graph illustrating how it looks:

Gaussian function

We will use our Gaussian function in a way, that the value will be the biggest when offset is equal to 0.5 (the top of the bell) and the further from 0.5 Gaussian function will return values closer to 0. Having that, we are going to make use of Transform widget using simple translation to change widgets’ position based on the Gaussian value and move them right. The code will make it clear:

All we needed to do is wrapping the texts Transform.translate and now we can see that when user scrolls, those elements tend to move a bit horizontally.

Having all the new translations we can look at the final result:

Final result
The original design

That’s it!

We have successfully added the parallax effect to our cards using only PageController, Alignment and Transform.translate!

I hope you enjoyed this post, if so please leave the star in the GitHub repo 🙂

If you have any questions, feel free to leave a comment.

Cheers 🙂

You can find part 2 of this challenge in here and other UI Challenges on my blog in here.

23 thoughts on “Parallax effect in PageView – UI Tickets Challenge

  1. hii nice work men ..i love ur works …
    and if its not too much to ask am kinda more interested on bottom to top scrolling part, can you tell how you do that… i was doing something with that too….

    thanks anyways

  2. Hi, I have a question, I would like it when you click on the “reserve” button, browse each card on a different page, what should you do?

    1. Well, obviously it depends on what you need exactly but you can take the card widget and put inside the new page using some new Scaffold and Navigator.of(context).push.

  3. I think you can use offset: math.sin(offset.abs() * math.pi) instead of gauss function for easier to understand

  4. Are you ever gonna show us the Pro solution, with the AnimatedBuilder?

    This solution quickly runs into performance issues sadly.

Leave a Reply

Your email address will not be published.