[Flutter] Create a piano app from scratch
I’m trying to learn new languages, so I read a lot about JavaScript and TypeScript at the moment.
The other day, I came across this video — Build a Piano with JavaScript — and wondered if I could redo this using only Flutter and a few classes… Maybe it could come in handy to one of you guys!
So let’s do it! Let’s create a new Flutter project from scratch with the flutter create flutter_piano
!
First of all, we need some notes. Let’s start by downloading this archive here : https://github.com/nsalleron/flutter_piano/blob/main/notes.zip, add it to the Assets folder.
Don’t forget to reference the Assets folder in the pubspec.yaml. We need to do this, otherwise our assets won’t be found at runtime.
flutter:
assets:
- assets/notes/
After this, don’t forget to add the assets_audio_player dependencies as well, in pubspec.yaml:
dependencies:
assets_audio_player: ^3.0.3+6
flutter:
sdk: flutter
Alright, let’s dive into it !
First, we have to create a StatefullWidget, because a note needs to be played when a user taps on a key.
Now to the design of our tiles. We have Blue(Black) tiles and White tiles, which we need to make responsive: we don’t want a key to disappear when we rotate the screen, plus we want our piano to render well on a variety of devices.
Flutter provides a Widget called LayoutBuilder. This widget allows us to know the constraints of a screen — in our case, the maxWidth for example. Check out this video for a nice recap of LayoutBuilder:
With the maxWidth information, we now know the width of each white key (we got seven) and thanks to LayoutBuilder, it’s fully responsive.
Because black keys lay over white ones, the use of Stack Widget will allow us to position its children relatively to the edges of its box. This class is useful if you want to overlap several children easily; for example, if you want some text to overlap an image.
We need a Positioned Widget to help us place each key next to the other. Each key has a position, and the width of each key depends on the constraints we’ve just seen.
Our design is really simple — just white with a black border, nothing too fancy :o)
Black keys are not difficult to implement; we just need one more information. Since they are smaller than white keys, we get this info from constraints.maxHeight. In this case, we’ll divide this value by two.
Let’s call these keys in our main screen!
Ok! We’ve got our white and blue(black) keys, so let’s launch the app:
Everything seems to be fine, but we forgot to use the AssetsAudioPlayer. Just declare :
final assetsAudioPlayer = AssetsAudioPlayer();
at the top of the _FlutterPianoScreenState classes and create a function _readNote(String note) to read the note from the assets.
void _readNote(String note) {
assetsAudioPlayer.open(
Audio("assets/notes/$tone.mp3"),
);
}
Don’t forget to call this function in each key with the onPressed callback :)
onPressed: () => _readTone('$tone'),
And that’s it, we have a little piano ! Let’s play!
We can’t really play with the keyboard yet, though… we can only tap a key with the mouse, and it isn’t very practical — is it? It’s because we didn’t configure the keyboard. We can work this out by using RawKeyboardListener.
This widget calls a callback function whenever the user presses or releases a key. Let’s wrap our stack with the RawKeyboardListener widget :
We need to place autofocus parameter at true, because we want to focus the entire screen. We also have _focusNode and _handleKeyEvent. The first variable allows us to receive key events that focus on this node when the second is a function that will call _readNote if the correct key is pressed.
And that’s really it! Now you can play :)
You can find the full code here : https://github.com/nsalleron/flutter_piano