UI Challenge: Piano Tiles clone in Flutter
Hi guys! This post is pretty straight-forward. I wanted to create a basic clone of Piano Tiles 2 — a mobile game where you play on a simplified piano. Now I want to share with you how to do that in very few steps using Flutter. Let’s get started!
TLDR: Piano Tiles 2 clon app in Flutter, code available here.
Setup
Model
Let’s start with setting basic Note
model we will be using:
Every note is represented by its number in the song, the line (tone?) in which it should appear (from 0 to 3) and its state which can be modified from ready to tapped or missed.
The song can look like that:
App structure
App structure is fairly simple, we will have a MaterialApp
with one screen:
We use Material
so that we can access nice Texts
and AlertDialogs
, normally you would use Scaffold
, which also provides a Material
to draw on, but we don’t really need it, so we will use simple Material. We also use Stack
– it gives us a way to put widgets on top of each other. Right now what we want to stack is a Row
containing empty lines and LineDividers
on top of the background image.
This is our starting point:
. . .
Drawing tiles
Tile Widget
Let’s start with a Tile
widget which will be a simple rectangle of a specific height. Depending on note’s state it can be black, transparent or red.
Line Widget
Now let’s move to Widget representing one of 4 lines in the game. Line
widget will accept 4 current notes at the time and it will draw Tiles
representing each note from with matching line (tone) number:
As you can see, for each note we can get what index that note has, therefore we can calculate how much it should be moved from top using Transform.translate
.
Now all we just need to use Line
widget in MainPage
:
And we can see the first 4 notes displayed:
. . .
Moving the Tiles
So far, we are always using the first 4 notes: notes.sublist(0, 4)
. To change that, we need to introduce a variable that will store the current index to be displayed. We will also need to periodically increment it. To do that, we will use AnimationController
and add a listener to it, so that on every completed animation, currentNote will increase:
Having that, we can see how tiles are moving:
Animated movement
As you can see, the Tiles are moving but they lack smoothness. Let’s deal with it now.
At first, we need to notice, that in order for a Line
to display moving Tiles, it has to have access to 5 of them instead of 4, because once one tile starts to disappear in the bottom the other one has to show from the top.
What is also worth noting is that each Line
should be rebuilt on every animation change. To easily achieve that, we can change Line’s superclass from StatelessWidget to AnimatedWidget, which ensures that on every animation tick, the widget will be drawn again. Also, having access to animation will let us easily adjust translate offsets we calculated earlier – let’s see the code:
Now we just need to add small adjustments in HomePage:
We can see nice, animated, moving tiles!
. . .
Tap handling
Now it’s time to handle user taps on tiles. We are going to handle only taps on tiles — not outside of them. To do that we need to use GestureDetector
. In the first version of this app, which I showed in my Twitter post, I used GestureDetector’s onTap
method. This method gets called, when a child is tapped, meaning when a user quickly puts a finger on a child and the takes it from the child. Seems right, doesn’t it? Except, that if a user releases the touch outside the child, onTap
will not get called. Having in mind that our Tiles are moving quickly, it was rather common to touch the Tile, but to pull the finger up when the Tile was no longer there, this way the tap method was not called and the user was frustrated because he knows he tapped it. There is a quick solution to that: instead of using onTap
, we will use onTapDown
which is called as soon as the user touches the child.
Let’s see the code:
This way, when a user taps on a Tile, we change its state to tapped, which means it will be transparent, like that:
Now we can add few adjustments to handling taps.
Point score
That one is rather simple, let’s have a variable that stores points, display it on the screen and increase it every time the user taps a tile.
Ensure tap order
This way, a tap is only handled if all previous tiles were tapped.
Starting game on tap
Right now the game starts when the screen gets initialized. We can add a flag, so that the first tap will cause animationController to start.
Play a note!
I am not a musician and I have absolutely no idea what sounds should I use, however, I found some samples I assumed I can use. We will use audioplayers package created by Luan Nico. At first, we need to add the dependency and sound files to the pubspec.yaml
file. You can find the notes in my GitHub repository.
Now we can play a note on tap:
I will not include a video with sound, if you want, you can clone the repo and build it by yourself ? Warning: It ain’t Mozart.
. . .
Finishing the game
The last part is to add logic for finishing the game. As said earlier, we won’t be handling taps on wrong lines, so the only way to lose is to miss the Tile. Implementing that is very easy, all we need to do is modify out animation’s status listener so that it also checks if current(last) tile was tapped, if it wasn’t it means it was missed, meaning game over. To stop the game, we will add isPlaying
flag, we will check it during animation updates and we will set it to false when the game is over.
This code only stops the game, in Piano Tiles 2, there is also an animation which indicates what Tile you missed. Theoretically, it might be a problem, as the Tile we missed is already gone, however, if we reverse
the animationController, then we should be able to see it again. What is more, we can set the Tile’s state to missed
so that it will turn red.
Hmm… since we are on the run, why not create a simple popup with a number of achieved points?
. . .
Start it over!
The last thing we want to do is add a simple restart function so that we can keep on playing!
. . .
And that’s it!
We created a simplified Piano Tiles app with smooth animations, sounds and score system. It may lack some features but I guess it is enough to say, that with Flutter sky is the limit. ?
I hope you enjoyed this post as much as I enjoyed writing it. If you have any questions, feel free to comment!
You can see the full code on GitHub >> here <<. If you liked the topic, you can leave a star on GitHub to let me know ?
Be sure to stay updated with my posts on Twitter (@marcin_szalek), Facebook (@mszalekblog) and Medium (@mszalek)
Cheers 🙂
Marcin Szałek
Founder of Fidev
Flutter enthusiast since Alpha release in 2017. Believes that sharing is caring, which lead him to start a technical blog dedicated fo Flutter in its early days. Loves to see beautiful designs become real apps and is willing to help make it happen. Enjoys sunny beaches far from home.
Join the newsletter!
Join the newsletter to keep track with latest posts and get my special Widget to animate views' entrances without any hassle for FREE!
Check out other posts!
Stripe Checkout in Flutter Web
Flutter Web is getting more mature every day. If you want to accept payments using Stripe Checkout in your Flutter web application, this article is just for you!
Stripe Checkout in mobile Flutter app
Have you ever struggled to integrate card payments into your mobile Flutter app? If so, today is your lucky day! In this post, I present how to use Stripe Checkout in the Flutter app without any hassle!
Interacting with Widgets using Framy
Have you ever developed a widget or a page and you wanted to make sure it works correctly in different scenarios but then it turned out that you can’t just reproduce all the cases you want to cover? Framy may solve such problems!
Nice article! Thanks
🙂
Good article One tip; You can use;
VerticalDivider(), instead of your own created LineDivider.
Good article One tip; You can use;
VerticalDivider(), instead of your own created LineDivider.
Cool! Didn’t know about it! Thank you! 🙂
Wonderful. Learned a lot.
🙂
Wonderful. Learned a lot.
🙂
Hello, love to read your article and get so much information through your blog and learn new things. You write very well, am amazed by your blogging, you will definitely achieve success.
Thanks 🙂
Hello, love to read your article and get so much information through your blog and learn new things. You write very well, am amazed by your blogging, you will definitely achieve success.
Thanks 🙂
Great 👍😍
Great ??
Thanks 🙂
Hi,
for app thanks.
Question:
How the first tile falls from the top??
What do you mean? First tile is actually waiting at the bottom
Hi,
for app thanks.
Question:
How the first tile falls from the top??
What do you mean? First tile is actually waiting at the bottom
wonderful, Tks to share the knowledge. Appreciate that man.
You’re welcome 🙂
wonderful, Tks to share the knowledge. Appreciate that man.
You’re welcome 🙂