flutter_notemus 2.2.0
flutter_notemus: ^2.2.0 copied to clipboard
Professional music notation rendering for Flutter. SMuFL-compliant with 2932 Bravura glyphs, beams, dynamics, articulations, and JSON import/export.
Flutter Notemus #
Professional music notation rendering for Flutter with SMuFL-compliant engraving, Bravura glyph support, and a first-party notation-to-MIDI pipeline.
Project Links #
- GitHub repository: https://github.com/alessonqueirozdev-hub/flutter_notemus
- pub.dev package: https://pub.dev/packages/flutter_notemus
- GitHub Pages (site/build): https://alessonqueirozdev-hub.github.io/flutter_notemus/
- Issue tracker: https://github.com/alessonqueirozdev-hub/flutter_notemus/issues
Table of Contents #
- Current Status
- Open Pending Work
- Highlights
- Installation
- Required Initialization
- Quick Start
- API Guide
- MusicScore Widget
- Pitch and Notes
- Durations
- Rests
- Measures and Staff
- Clefs
- Key Signatures
- Time Signatures
- Barlines
- Chords
- Ties and Slurs
- Articulations
- Dynamics
- Ornaments
- Tempo Marks
- Grace Notes
- Tuplets
- Beams
- Octave Markings
- Volta Brackets
- Polyphony and Multi-Voice
- Repeats
- Playing Techniques
- Breath and Caesura
- Import from JSON, MusicXML, and MEI
- MIDI Mapping and Export
- Themes and Styling
- Reference JSON Format
- Architecture
- Development Checklist
- License
Current Status #
- Current package release target:
2.2.0 - Previous pub.dev baseline before the new generation:
0.1.0 - Core notation rendering is production-ready.
- MIDI mapping and
.midexport are available in the package. - Android native audio backend is active; other native targets are configured and tracked as pending.
Open Pending Work #
All pending work is tracked as GitHub issues:
- Native audio backend parity (iOS/macOS/Linux/Windows): https://github.com/alessonqueirozdev-hub/flutter_notemus/issues/1
- Real notation engraving for PDF export: https://github.com/alessonqueirozdev-hub/flutter_notemus/issues/2
- SMuFL brace workflow for staff groups: https://github.com/alessonqueirozdev-hub/flutter_notemus/issues/3
- Stem/flag primitive parameterization via engraving defaults: https://github.com/alessonqueirozdev-hub/flutter_notemus/issues/4
- Robust fallback for
repeatBoth: https://github.com/alessonqueirozdev-hub/flutter_notemus/issues/5
Highlights #
Core notation #
- Notes from whole through 1024th durations
- Rests for all supported durations
- Accidentals (natural, sharp, flat, double sharp, double flat)
- Automatic ledger lines
Clefs #
- Treble, bass, alto, tenor, percussion, tablature
- Octave-transposing clef variants (8va, 8vb, 15ma, 15mb)
Rhythm and layout #
- Proportional rhythmic spacing
- Auto and manual beaming
- Tuplet support
- Collision-aware layout
- Multi-measure and multi-system rendering
Symbols and expression #
- Dynamics and hairpins
- Articulations
- Ornaments
- Tempo marks and text
- Ties and slurs
- Volta brackets, repeat symbols, and structural barlines
- Octave markings
Multi-staff and polyphony #
- Multiple voices in a single staff (
MultiVoiceMeasure) - Multi-staff score support (
Score,StaffGroup) - Grand staff scenarios (piano)
- SATB-style aligned staff rendering
Import and interoperability #
- JSON parser
- MusicXML parser (
score-partwiseandscore-timewise) - MEI parser
- Unified normalization to the same internal model
MIDI pipeline #
- Notation-to-MIDI mapping from
StaffandScore - Repeat and volta expansion for playback timeline
- Tuplet/polyphony/tie-aware event generation
- Metronome track generation
- Standard MIDI file export (
MidiFileWriter)
Installation #
Add dependency to your pubspec.yaml:
dependencies:
flutter_notemus: ^2.2.0
Install packages:
flutter pub get
Required Initialization #
Load Bravura and SMuFL metadata before rendering any score:
import 'package:flutter/services.dart';
import 'package:flutter_notemus/flutter_notemus.dart';
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
final loader = FontLoader('Bravura');
loader.addFont(
rootBundle.load('packages/flutter_notemus/assets/smufl/Bravura.otf'),
);
await loader.load();
await SmuflMetadata().load();
runApp(const MyApp());
}
Quick Start #
import 'package:flutter/material.dart';
import 'package:flutter_notemus/flutter_notemus.dart';
class SimpleScorePage extends StatelessWidget {
const SimpleScorePage({super.key});
@override
Widget build(BuildContext context) {
final staff = Staff();
final measure = Measure();
measure.add(Clef(clefType: ClefType.treble));
measure.add(TimeSignature(numerator: 4, denominator: 4));
measure.add(Note(
pitch: const Pitch(step: 'C', octave: 5),
duration: const Duration(DurationType.quarter),
));
measure.add(Note(
pitch: const Pitch(step: 'E', octave: 5),
duration: const Duration(DurationType.quarter),
));
measure.add(Note(
pitch: const Pitch(step: 'G', octave: 5),
duration: const Duration(DurationType.quarter),
));
measure.add(Note(
pitch: const Pitch(step: 'C', octave: 6),
duration: const Duration(DurationType.quarter),
));
staff.add(measure);
return Scaffold(
body: SizedBox(
height: 180,
child: MusicScore(staff: staff),
),
);
}
}
API Guide #
MusicScore Widget #
MusicScore(
staff: staff,
staffSpace: 12.0,
theme: const MusicScoreTheme(),
)
Pitch and Notes #
Note(
pitch: const Pitch(step: 'G', octave: 4),
duration: const Duration(DurationType.quarter),
articulations: [ArticulationType.staccato],
tie: TieType.start,
slur: SlurType.start,
beam: BeamType.start,
)
Accidentals are encoded directly in Pitch:
const Pitch(step: 'F', octave: 5, alter: 1.0); // sharp
const Pitch(step: 'B', octave: 4, alter: -1.0); // flat
const Pitch(step: 'C', octave: 5, alter: 2.0); // double sharp
const Pitch(step: 'D', octave: 4, alter: -2.0); // double flat
Durations #
const Duration(DurationType.whole);
const Duration(DurationType.half);
const Duration(DurationType.quarter);
const Duration(DurationType.eighth);
const Duration(DurationType.sixteenth);
const Duration(DurationType.thirtySecond);
const Duration(DurationType.sixtyFourth);
const Duration(DurationType.oneHundredTwentyEighth);
Dotted durations:
const Duration(DurationType.quarter, dots: 1);
const Duration(DurationType.half, dots: 2);
Rests #
Rest(duration: const Duration(DurationType.whole));
Rest(duration: const Duration(DurationType.half));
Rest(duration: const Duration(DurationType.eighth));
Measures and Staff #
final staff = Staff();
final measure = Measure();
measure.add(Clef(clefType: ClefType.treble));
measure.add(TimeSignature(numerator: 3, denominator: 4));
measure.add(Note(
pitch: const Pitch(step: 'C', octave: 5),
duration: const Duration(DurationType.quarter),
));
staff.add(measure);
Clefs #
Clef(clefType: ClefType.treble);
Clef(clefType: ClefType.treble8vb);
Clef(clefType: ClefType.bass);
Clef(clefType: ClefType.alto);
Clef(clefType: ClefType.tenor);
Clef(clefType: ClefType.percussion);
Clef(clefType: ClefType.tab6);
Key Signatures #
KeySignature(0); // C major / A minor
KeySignature(2); // D major (2 sharps)
KeySignature(-3); // E-flat major (3 flats)
Time Signatures #
TimeSignature(numerator: 4, denominator: 4);
TimeSignature(numerator: 3, denominator: 4);
TimeSignature(numerator: 6, denominator: 8);
Barlines #
Barline(type: BarlineType.single);
Barline(type: BarlineType.double);
Barline(type: BarlineType.final_);
Barline(type: BarlineType.repeatForward);
Barline(type: BarlineType.repeatBackward);
Barline(type: BarlineType.repeatBoth);
Chords #
Chord(
notes: [
Note(pitch: const Pitch(step: 'C', octave: 4), duration: const Duration(DurationType.half)),
Note(pitch: const Pitch(step: 'E', octave: 4), duration: const Duration(DurationType.half)),
Note(pitch: const Pitch(step: 'G', octave: 4), duration: const Duration(DurationType.half)),
],
duration: const Duration(DurationType.half),
);
Ties and Slurs #
Note(
pitch: const Pitch(step: 'C', octave: 5),
duration: const Duration(DurationType.half),
tie: TieType.start,
);
Note(
pitch: const Pitch(step: 'D', octave: 5),
duration: const Duration(DurationType.quarter),
slur: SlurType.start,
);
Articulations #
Note(
pitch: const Pitch(step: 'G', octave: 4),
duration: const Duration(DurationType.quarter),
articulations: [
ArticulationType.staccato,
ArticulationType.accent,
ArticulationType.tenuto,
ArticulationType.marcato,
],
);
Dynamics #
Dynamic(type: DynamicType.piano);
Dynamic(type: DynamicType.mezzoForte);
Dynamic(type: DynamicType.forte);
Dynamic(type: DynamicType.crescendo);
Dynamic(type: DynamicType.diminuendo);
Ornaments #
Ornament(type: OrnamentType.trill);
Ornament(type: OrnamentType.mordent);
Ornament(type: OrnamentType.turn);
Ornament(type: OrnamentType.fermata);
Tempo Marks #
TempoMark(
bpm: 120,
beatUnit: DurationType.quarter,
text: 'Allegro',
);
Grace Notes #
GraceNote(
pitch: const Pitch(step: 'D', octave: 5),
type: GraceNoteType.acciaccatura,
);
Tuplets #
Tuplet(
actualNotes: 3,
normalNotes: 2,
elements: [
Note(pitch: const Pitch(step: 'C', octave: 5), duration: const Duration(DurationType.eighth)),
Note(pitch: const Pitch(step: 'D', octave: 5), duration: const Duration(DurationType.eighth)),
Note(pitch: const Pitch(step: 'E', octave: 5), duration: const Duration(DurationType.eighth)),
],
);
Beams #
Note(
pitch: const Pitch(step: 'E', octave: 5),
duration: const Duration(DurationType.eighth),
beam: BeamType.start,
);
Octave Markings #
OctaveMark(type: OctaveType.ottava);
OctaveMark(type: OctaveType.ottavaBassa);
OctaveMark(type: OctaveType.quindicesima);
OctaveMark(type: OctaveType.quindicesimaBassa);
Volta Brackets #
VoltaBracket(number: 1, length: 220);
VoltaBracket(number: 2, length: 180, hasOpenEnd: true);
Polyphony and Multi-Voice #
final measure = MultiVoiceMeasure();
final voice1 = Voice.voice1();
voice1.add(Clef(clefType: ClefType.treble));
voice1.add(TimeSignature(numerator: 4, denominator: 4));
voice1.add(Note(
pitch: const Pitch(step: 'E', octave: 5),
duration: const Duration(DurationType.quarter),
));
final voice2 = Voice.voice2();
voice2.add(Note(
pitch: const Pitch(step: 'C', octave: 4),
duration: const Duration(DurationType.half),
));
measure.addVoice(voice1);
measure.addVoice(voice2);
Repeats #
Barline(type: BarlineType.repeatForward);
Barline(type: BarlineType.repeatBackward);
Barline(type: BarlineType.repeatBoth);
Playing Techniques #
PlayingTechnique(type: TechniqueType.pizzicato);
PlayingTechnique(type: TechniqueType.colLegno);
PlayingTechnique(type: TechniqueType.sulTasto);
PlayingTechnique(type: TechniqueType.sulPonticello);
Breath and Caesura #
Breath();
Breath(type: BreathType.caesura);
Breath(type: BreathType.shortBreath);
Import from JSON, MusicXML, and MEI #
final jsonStaff = JsonMusicParser.parseStaff(jsonString);
final musicXmlStaff = MusicXMLParser.parseMusicXML(musicXmlString);
final meiStaff = MEIParser.parseMEI(meiString);
final autoDetected = NotationParser.parseStaff(sourceString);
MIDI Mapping and Export #
import 'dart:io';
import 'package:flutter_notemus/midi.dart';
Future<void> exportMidi(Staff staff) async {
final sequence = MidiMapper.fromStaff(
staff,
options: const MidiGenerationOptions(
ticksPerQuarter: 960,
defaultBpm: 120,
includeMetronome: true,
),
);
final bytes = MidiFileWriter.write(sequence);
await File('score.mid').writeAsBytes(bytes, flush: true);
}
Native integration APIs:
MidiNativeAudioBackendMethodChannelMidiNativeAudioBackendMidiNativeSequenceBridge
Themes and Styling #
MusicScore(
staff: staff,
theme: const MusicScoreTheme(
staffLineColor: Colors.black,
noteheadColor: Colors.black,
stemColor: Colors.black,
clefColor: Colors.black,
barlineColor: Colors.black,
dynamicColor: Colors.black,
tieColor: Colors.black,
slurColor: Colors.black,
textColor: Colors.black,
),
)
Reference JSON Format #
{
"measures": [
{
"clef": "treble",
"timeSignature": { "numerator": 4, "denominator": 4 },
"keySignature": 0,
"elements": [
{ "type": "note", "step": "C", "octave": 5, "duration": "quarter" },
{ "type": "note", "step": "E", "octave": 5, "duration": "quarter" },
{ "type": "note", "step": "G", "octave": 5, "duration": "quarter" },
{ "type": "rest", "duration": "quarter" }
]
}
]
}
Architecture #
flutter_notemus/
├── lib/
│ ├── flutter_notemus.dart # Public entry point
│ ├── midi.dart # Public MIDI entry point
│ ├── core/ # Music model
│ └── src/
│ ├── midi/ # MIDI mapping/export/native bridge
│ ├── layout/ # Layout and spacing
│ ├── rendering/ # Renderers and positioning
│ ├── smufl/ # Metadata and glyph references
│ ├── parsers/ # JSON, MusicXML, MEI parsers
│ └── theme/ # Theme and style system
├── assets/smufl/ # Bravura + metadata
├── android/ios/macos/linux/windows # Plugin/native targets
└── example/ # Demo application
Rendering flow:
Staff/Score -> LayoutEngine -> PositionedElements -> StaffRenderer -> Canvas
Development Checklist #
Typical local validation:
dart analyze
flutter test
flutter pub publish --dry-run
License #
- Project code: Apache License 2.0 (
LICENSE) - Bravura font: SIL Open Font License 1.1
- SMuFL specification: W3C Music Notation Community Group