Skip to content
A Crossplatform Piano made with Flutter
Branch: master
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Type Name Latest commit message Commit time
Failed to load latest commit information.
android Updating May 12, 2019
assets Create repository-open-graph-template.png May 7, 2019
docs adding docs May 12, 2019
icons adding icons/ May 12, 2019
lib Updating May 12, 2019
linux Updating for Desktop May 7, 2019
macos Updating for Desktop May 7, 2019
screenshots Adding Screenshots Feb 22, 2019
test Updating Tests May 7, 2019
test_driver Adding Base Tests May 7, 2019
web adding web May 10, 2019
windows Updating for Desktop May 7, 2019
.gitattributes Initial commit Feb 22, 2019
.gitignore Initial commit Feb 22, 2019
.metadata Initial commit Feb 22, 2019
LICENSE Update LICENSE Feb 28, 2019 Update May 12, 2019
pubspec.lock Updating May 12, 2019
pubspec.yaml Update pubspec.yaml May 12, 2019
screenshots.yaml Update screenshots.yaml May 7, 2019

Codemagic build status


A Crossplatform Midi Piano built with

  • This application runs on both iOS and Android.
  • This runs a custom crossplatform midi synth I built for a Flutter plugin flutter_midi that uses .SF2 sound font files.

The Pocket Piano by Rody Davis App Store | Google Play

   - assets/sounds/Piano.SF2

  • There are Semantics included for the visually impaired. All keys show up as buttons and have the pitch name of the midi note not just the number.

Getting Started

This application only runs in landscape mode, orientation is set in the AndroidManifest.xml and in the Runner.xcworspace settings.

  1. Make sure to turn your volume up and unmute the phone (the application will try to unmute the device but it can be overriden).
  2. Tap on any note to play
  3. Scroll in either direction to change octaves
  4. Polyphony is supported with multiple fingers


  • Optionally the key width can be changed in the settings for adjusting key densitity.

  • The key labels can also be turned off if you want a more minimal look.





  • You can change the Piano.sf2 file to any sound font file for playing different instruments.



alt-text-1 alt-text-1


alt-text-2 alt-text-2


import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_midi/flutter_midi.dart';
import 'package:tonic/tonic.dart';

void main() => runApp(MyApp());

class MyApp extends StatefulWidget {
 _MyAppState createState() => _MyAppState();

class _MyAppState extends State<MyApp> {
 initState() {
   rootBundle.load("assets/sounds/Piano.SF2").then((sf2) {
     FlutterMidi.prepare(sf2: sf2, name: "Piano.SF2");

 double get keyWidth => 80 + (80 * _widthRatio);
 double _widthRatio = 0.0;
 bool _showLabels = true;

 Widget build(BuildContext context) {
   return MaterialApp(
     title: 'The Pocket Piano',
     theme: ThemeData.dark(),
     home: Scaffold(
         drawer: Drawer(
             child: SafeArea(
                 child: ListView(children: <Widget>[
           Container(height: 20.0),
           ListTile(title: Text("Change Width")),
               activeColor: Colors.redAccent,
               inactiveColor: Colors.white,
               min: 0.0,
               max: 1.0,
               value: _widthRatio,
               onChanged: (double value) =>
                   setState(() => _widthRatio = value)),
               title: Text("Show Labels"),
               trailing: Switch(
                   value: _showLabels,
                   onChanged: (bool value) =>
                       setState(() => _showLabels = value))),
         appBar: AppBar(title: Text("The Pocket Piano")),
         body: ListView.builder(
           itemCount: 7,
           controller: ScrollController(initialScrollOffset: 1500.0),
           scrollDirection: Axis.horizontal,
           itemBuilder: (BuildContext context, int index) {
             final int i = index * 12;
             return SafeArea(
               child: Stack(children: <Widget>[
                 Row(mainAxisSize: MainAxisSize.min, children: <Widget>[
                   _buildKey(24 + i, false),
                   _buildKey(26 + i, false),
                   _buildKey(28 + i, false),
                   _buildKey(29 + i, false),
                   _buildKey(31 + i, false),
                   _buildKey(33 + i, false),
                   _buildKey(35 + i, false),
                     left: 0.0,
                     right: 0.0,
                     bottom: 100,
                     top: 0.0,
                     child: Row(
                         mainAxisAlignment: MainAxisAlignment.spaceBetween,
                         mainAxisSize: MainAxisSize.min,
                         children: <Widget>[
                           Container(width: keyWidth * .5),
                           _buildKey(25 + i, true),
                           _buildKey(27 + i, true),
                           Container(width: keyWidth),
                           _buildKey(30 + i, true),
                           _buildKey(32 + i, true),
                           _buildKey(34 + i, true),
                           Container(width: keyWidth * .5),

 Widget _buildKey(int midi, bool accidental) {
   final pitchName = Pitch.fromMidiNumber(midi).toString();
   final pianoKey = Stack(
     children: <Widget>[
           button: true,
           hint: pitchName,
           child: Material(
               borderRadius: borderRadius,
               color: accidental ? : Colors.white,
               child: InkWell(
                 borderRadius: borderRadius,
                 highlightColor: Colors.grey,
                 onTap: () {},
                 onTapDown: (_) => FlutterMidi.playMidiNote(midi: midi),
           left: 0.0,
           right: 0.0,
           bottom: 20.0,
           child: _showLabels
               ? Text(pitchName,
                   style: TextStyle(
                       color: !accidental ? : Colors.white))
               : Container()),
   if (accidental) {
     return Container(
         width: keyWidth,
         margin: EdgeInsets.symmetric(horizontal: 2.0),
         padding: EdgeInsets.symmetric(horizontal: keyWidth * .1),
         child: Material(
             elevation: 6.0,
             borderRadius: borderRadius,
             shadowColor: Color(0x802196F3),
             child: pianoKey));
   return Container(
       width: keyWidth,
       child: pianoKey,
       margin: EdgeInsets.symmetric(horizontal: 2.0));

const BorderRadiusGeometry borderRadius = BorderRadius.only(
   bottomLeft: Radius.circular(10.0), bottomRight: Radius.circular(10.0));

Total Dart Code Size: 5039 bytes

Special Thenks

  • @DFreds
  • @jesusrp98
You can’t perform that action at this time.