The finished product
https://www.youtube.com/watch?v=FiJQdFeo8kk
It’s been a while since I did one of these. As a big F1 fan, when I saw this awesome Dribbble by Pontus Wellgraf, I had to try and make it in Flutter.
Imagery
The design calls for the driver to be half on screen, luckily the imagery on formula1.com has all the drivers centered. So the next step was to get them into a pager to nicely swipe.
Paging
At first I went for a PageView
widget. This sort of worked, but gave me problems. The image needs to take up 2 screen widths to make sure the driver is in the middle. My first attempt was to change the viewportFraction
to 2.0 in a PageController
. This worked, but not for the first page .. and if memory serves me the last too. After playing with various settings, I came to the conclusion that instead I needed to use a ListView
.
With the ListView
is was able to set the image size to 2 screen widths by setting the Image
‘s width property to:MediaQuery.of(context).size.width*2
.
Of course the ListView
scrollDirection is also set to Axis.horizontal
.
So now I can scroll the photos, but need to make them snap into place, enter ScrollPhysics
.
To cut a long story short, I needed to snap to every second page. The easiest way I found was to copy the PageScrollPhysics
class and make a couple of adjustments. See F1ScrollPhysics
in the repo. Once that was applied the pages snap correctly and all is well.
Animation
Onto the fun part, the character animations.
The first part is figuring out how far we have scrolled, from which driver and to which driver. To do that I added a ScrollController
to the ListView. Adding a listener gives us updates every time the ListView scrolls.
As we are scrolling 2 pages at a time, some maths is required to know where we are:
_scrollController.offset / (MediaQuery.of(context).size.width * 2)
This will give us a fractional position of where we are, so 0.5 is half way between item 0 and item 1.
Changing the driver number
Starting on the driver number seemed like a sensible choice, it’s always an Integer. As we get updates very often from the ScrollController, we should just be able to change the String and update State as we go. No Animation Controller needed.
We need to get the last driver number and the next driver number. To do this I used floor
and ceil
on the pageFraction. Once we have these we can then use pageFraction%1
to know how far between we need to be, and calculate with:
lastDriverNumber-((lastDriverNumber-nextDriverNumber)*currentFraction).round()
Applying this to State, gives us the required effect. The number changes as we scroll.
Changing the rest
Next we need to change the driver’s first name, last name, and team. As these are characters … its a bit more complicated, but not too much.
First I created a string with all characters (only used the English alphabet, sorry Mr Räikkönen). All chars in upper case, lower case, and a space. This was enough for the proof of concept. Obviously more would be needed in reality.
I used the same calculations as the driver number, then found the characters between using this string and the fraction. See the _calculateCharacters
in the repo for more details.
The Repo
All open source, do with it what you will … https://github.com/tunitowen/f1_animation