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 Flutter package for working with piano keys and sheet music

Piano A Flutter package that provides: logic for working with musical notes, clefs and octaves; a widget that can render notes on a clef; an interacti

Craig McMahon 32 Jan 5, 2023
Relive is a cross-platform application that can be used both in Android/IOS and it is made using Flutter

?? Introduction Relive is a cross-platform application that can be used both in Android/IOS and it is made using Flutter. It aims to solve the mental

Akshat Tripathi 14 Nov 4, 2022
A simple, cross-platform password manager created with Flutter.

PassMan PassMan Logo © 2021 by Yash Ahir is licensed under CC BY-NC 4.0 A simple, cross-platform password manager created with Flutter. How to run thi

Yash Ahir 19 Dec 14, 2022
This is the new version of my Task app "Tasko" which was done in Java. She is now in Flutter for the HotReload and the native Cross-Platform.

tasko_rem The Tasko App is now compatible on iOS, Android and others distribution, because it's made with Flutter ✨ You can now add task, check them o

Scythe 12 May 2, 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
Cross platform application for iOS and Android using Google's SDK Flutter.

scout Cross platform application for iOS and Android using Google's SDK Flutter. Launch screen for the application. The menu for selecting cookies. Cu

null 0 Nov 9, 2021
The Fuse Wallet is a cross platform Ethereum wallet written in Dart and built on Flutter.

Fuse Wallet The Fuse Wallet is a cross platform Ethereum wallet written in Dart and built on Flutter. It's runninng on the Fuse network, but can be pl

null 4 Nov 9, 2022
Super Fast Cross Platform Database for Flutter & Web Apps

Isar Database ?? Alpha version - Use with care. ?? Quickstart • Documentation • Sample Apps • Support & Ideas • Pub.dev Isar [ee-zahr]: River in Bavar

Isar Database 2.1k Jan 1, 2023
A cross platform application written in flutter to help people stick to their routines and achieve their goals

Scheduler Scheduler is a cross platform application written in flutter to help people stick to their routines and achieve their goals. Our service inc

Sidheshwar S 3 Jan 21, 2022
A cross platform todo list app using flutter and dart programming language

Flutter Todos A cross platform todo list app using flutter and dart programming language. In this application, I used SQLite3 to persist data. The app

Mahmud Ahsan 61 Dec 29, 2022
Quiz App is cross-platform mobile app, that allows you to test your knowledge on various technologies through quizzes. It's built with Flutter & Dart

Quiz App is cross-platform mobile app, that allows you to test your knowledge on various technologies through quizzes. It's built with Flutter & Dart

Régis 6 Sep 19, 2022
Cross-platform GitHub client in Flutter and BLoC clean architecture

FlutterHub Cross-platform GitHub client in Flutter and BLoC clean architecture. SwiftHub - Swift version is available Try Web version Get Started To g

Khoren Markosyan 36 Nov 12, 2022
A fully cross-platform wrap of the Matomo tracking client for Flutter, using the Matomo API.

A fully cross-platform wrap of the Matomo tracking client for Flutter, using the Matomo Tracking API.

Floating Dartists 12 Jan 8, 2023
Private, cross-platform package tracking app

LibreTrack Private, cross-platform package tracking app. Track postal items directly on your device using accounts of postal services. The app respect

Yaroslav Pronin 128 Jan 1, 2023
Cross-platform Twitch Chat application with 3rd-party addon support!

Chatsen Chatsen is a cross-platform application that allows you to chat on Twitch with support for 3rd-party services such as 7TV, BTTV and FFZ. It al

Chatsen 172 Dec 31, 2022
A cross platform GUI, soon to be the official GUI.

CCExtractor Flutter GUI The new cross platform interface is all you need, as it includes all the options. After installing GUI you will have a shortcu

CCExtractor Development 16 Nov 19, 2022
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 844 Jan 1, 2023
Drishti is an open-source cross-platform mobile application project at Incubate Nepal that incorporates Machine Learning and Artificial Intelligence

Drishti is an open-source cross-platform mobile application project at Incubate Nepal that incorporates Machine Learning and Artificial Intelligence to help visually impaired people recognize different currency bills and perform daily cash transactions more effectively. We plan to expand Drishti to other applications like Short Text and Document Reading in the future.

Drishti Nepal 23 Oct 8, 2022