A Cross Platform Piano made with Flutter

Overview

Codemagic build status

flutter_piano

A Crossplatform Midi Piano built with Flutter.dev

  • 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:
   - 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

Configuration

  • 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.

IOS

alt-text-1

Android

alt-text-2

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

Screenshots

iOS

alt-text-1 alt-text-1

Android

alt-text-2 alt-text-2

Code

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 {
 @override
 _MyAppState createState() => _MyAppState();
}

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

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

 @override
 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")),
           Slider(
               activeColor: Colors.redAccent,
               inactiveColor: Colors.white,
               min: 0.0,
               max: 1.0,
               value: _widthRatio,
               onChanged: (double value) =>
                   setState(() => _widthRatio = value)),
           Divider(),
           ListTile(
               title: Text("Show Labels"),
               trailing: Switch(
                   value: _showLabels,
                   onChanged: (bool value) =>
                       setState(() => _showLabels = value))),
           Divider(),
         ]))),
         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),
                 ]),
                 Positioned(
                     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>[
       Semantics(
           button: true,
           hint: pitchName,
           child: Material(
               borderRadius: borderRadius,
               color: accidental ? Colors.black : Colors.white,
               child: InkWell(
                 borderRadius: borderRadius,
                 highlightColor: Colors.grey,
                 onTap: () {},
                 onTapDown: (_) => FlutterMidi.playMidiNote(midi: midi),
               ))),
       Positioned(
           left: 0.0,
           right: 0.0,
           bottom: 20.0,
           child: _showLabels
               ? Text(pitchName,
                   textAlign: TextAlign.center,
                   style: TextStyle(
                       color: !accidental ? Colors.black : 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 Thanks

  • @DFreds
  • @jesusrp98
Comments
  • Reduce size of 5k branch from 5039 to 4918

    Reduce size of 5k branch from 5039 to 4918

    Summary of changes:

    • All doubles can be inferred without the dot zero at the end
    • Allowed dart to infer some types for the callbacks and build methods
    • Changed the border radius from 10 to 9 to save an additional 2 bytes for minimal UI change
    opened by DFreds 1
  • Plays keys forever for instruments that have loops

    Plays keys forever for instruments that have loops

    Describe the bug Instruments with loop plays forever if piano keys are pressed once.

    Expected behavior As soon as the key is released the audio should stop playing

    Smartphone (please complete the following information):

    • Device: OnePlus 7 Pro
    • OS: Android 11
    opened by ashishpatel1992 0
  • InkWell no use when tap piano key

    InkWell no use when tap piano key

    https://github.com/rodydavis/flutter_piano/blob/508b7dc47cecad5d92e4d1106c4fdefdbcd4b35f/lib/ui/common/piano_key.dart#L29 Hi, I found there is no touch ripples when tap Pianokey, maybe it is a bug. can you help to fix it, thanks.

    opened by wuxq 0
  • Delay after tapping piano key

    Delay after tapping piano key

    There is a significant delay after I tap a key on the piano before I hear the sound. 1/4 to 1/2 a second.

    My phone is a Pixel 3 XL with Android 9. I tried turning off hepatics in device settings and turning off feedback in the piano app settings.

    I have version 1.0.2 of the Pocket Piano. Thanks.

    opened by RichAJacobs 6
  • pianoKey label blocks interaction

    pianoKey label blocks interaction

    The key label blocks interaction with the button beneath. An easy fix is to wrap the label Text widget in an IgnorePointer widget.

    Thanks for the great work btw.

    opened by fideldonson 1
Owner
Rody Davis
Developer Advocate for @material-components at @Google
Rody Davis
A cross-platform (Windows/macOS) scanner plugin for Flutter

quick_scanner A cross-platform (Windows/macOS) scanner plugin for Flutter Usage QuickScanner.startWatch(); var _scanners = await QuickScanner.getScan

Woodemi Co., Ltd 5 Jun 10, 2022
A cross-platform (Android/Windows/macOS/Linux) USB plugin for Flutter

quick_usb A cross-platform (Android/Windows/macOS/Linux) USB plugin for Flutter Usage List devices List devices with additional description Get device

Woodemi Co., Ltd 39 Oct 1, 2022
Photon is a cross-platform file-sharing application built using flutter.

Welcome to Photon ?? Photon is a cross-platform file-transfer application built using flutter. It uses http to transfer files between devices.You can

Abhilash Hegde 161 Jan 1, 2023
🎵 A cross-platform media playback library for C/C++ with good number of features (only Windows & Linux).

libwinmedia A cross-platform media playback library for C/C++ & Flutter with good number of features. Example A very simple example can be as follows.

Harmonoid 38 Nov 2, 2022
A cross-platform app ecosystem, bringing iMessage to Android, PC (Windows, Linux, & even macOS), and Web!

BlueBubbles Android App BlueBubbles is an open-source and cross-platform ecosystem of apps aimed to bring iMessage to Android, Windows, Linux, and mor

BlueBubbles 318 Jan 8, 2023
Sharik is an open-source, cross-platform solution for sharing files via Wi-Fi or Mobile Hotspot

Share files across devices with Sharik! It works with Wi-Fi connection or Tethering (Wi-Fi Hotspot). No internet connection needed. Contributing Feel

Mark Motliuk 842 Dec 30, 2022
A platform adaptive Flutter app for desktop, mobile and web.

Flutter Folio A demo app showcasing how Flutter can deliver a great multi-platform experience, targeting iOS, Android, MacOS, Windows, Linux, and web.

gskinner team 3.5k Jan 2, 2023
Fluttern is a web app made with Flutter to list Flutter internships/jobs for the community.

Fluttern Fluttern is a web app made with Flutter to list Flutter internships/jobs for the community. It uses Google Sheet as a backend, simplifying th

Aditya Thakur 3 Jan 5, 2022
Youtube video downloader made using flutter.

FluTube Youtube video client made using flutter Features: Beautiful user interface Lightweight and fast No Login Required Keep your liked videos and c

Prateek SU 257 Jan 8, 2023
Just a jellyfin client made in flutter (side project quality code)

jellyflut A jellyfin client made in Flutter It's a beta, it works okay. You can : Play a video Read a book Listen Music Look photos SQLite to have log

Thomas SOHIER 106 Jan 4, 2023
Unofficial Ubuntu Desktop Settings App made with Flutter

Unofficial Ubuntu Desktop Settings App made with Flutter - WIP The goal of this project is to build a feature complete settings app for the Ubuntu des

Frederik Feichtmeier 239 Jan 1, 2023
Simple file explorer for desktop made with Flutter, highly inspired by macOS Finder

file_explorer A basic file explorer made with Flutter Getting Started This project is a starting point for a Flutter application. A few resources to g

Valentin 0 Nov 7, 2021
A small karaoke app made in Flutter for Linux

Karaoke app for linux A small karaoke/lyrics display for the currently running VLC song. Video: Make sure the songs have metadata (at least title and

Wazzaps 63 Jan 7, 2023
My Website made with Flutter 🚀

My Website ?? Layout ?? The layout was developed by Karol de Paula, and you can access it on Figma. ?? How to Use ?? First of all, correctly configure

Felipe Sales 4 Jun 13, 2022
Flutter Installer is an installer for Flutter built with Flutter 💙😎✌

Flutter Installer Flutter Installer is an installer for Flutter built with Flutter ?? ?? ✌ Flutter and the related logo are trademarks of Google LLC.

Yazeed AlKhalaf 406 Dec 27, 2022
A Flutter package that makes it easy to customize and work with your Flutter desktop app window.

bitsdojo_window A Flutter package that makes it easy to customize and work with your Flutter desktop app window on Windows, macOS and Linux. Watch the

Bits Dojo 607 Jan 4, 2023
Flutter plugin for Flutter desktop(macOS/Linux/Windows) to change window size.

desktop_window Flutter plugin for Flutter desktop(macOS/Linux/Windows) to change window size. Usage import 'package:desktop_window/desktop_window.dart

ChunKoo Park 72 Dec 2, 2022
A Flutter package that makes it easy to customize and work with your Flutter desktop app's system tray.

system_tray A Flutter package that that enables support for system tray menu for desktop flutter apps. on Windows, macOS and Linux. Features: - Modify

AnTler 140 Dec 30, 2022
Flutter on Windows, MacOS and Linux - based on Flutter Embedding, Go and GLFW.

go-flutter - A package that brings Flutter to the desktop Purpose Flutter allows you to build beautiful native apps on iOS and Android from a single c

null 5.5k Jan 6, 2023