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! 🙂
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 coming soon in the next post. 🙂
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:
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
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
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
Row with more
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
viewportFraction parameter, which determines how much space of the main axis should the one page take. In our case, it will be 80%.
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:
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:
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:
We have successfully added the parallax effect to our cards using only
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.
You can find more of my UI Challenges on my blog in here.