I'm trying to make a metronome in Flutter, but it isn't accurate for some reason. I'm using the AudioPlayers package to play the audio, and a timer to play the audio every n of milliseconds, based on the BPM set. The problem I'm facing is that the clicks aren't played at the right delay between each other.
Here is my code:
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:tools/components/appbar.dart';
import 'package:audioplayers/audioplayers.dart';
class MetronomeScreen extends StatefulWidget {
const MetronomeScreen({Key? key}) : super(key: key);
@override
State<MetronomeScreen> createState() => _MetronomeScreenState();
}
class _MetronomeScreenState extends State<MetronomeScreen> {
double bpm = 100;
bool isClicking = false;
Timer? timer;
final player = AudioPlayer();
@override
void initState() {
player.setSource(AssetSource('sounds/metronome.mp3'));
super.initState();
}
toggleMetronome() {
if (isClicking) {
print("Stopped Metronome");
timer?.cancel();
isClicking = false;
} else {
isClicking = true;
timer = Timer.periodic(
Duration(milliseconds: (60000 / bpm.round()).round()), (timer) {
player.resume();
});
}
}
@override
void dispose() {
timer?.cancel();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: ToolsAppBar(
title: "Metronome",
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
SizedBox(
width: 120,
child: Text(
"${bpm.round()} BPM",
style: const TextStyle(fontSize: 25),
)),
MaterialButton(
onPressed: () => toggleMetronome(),
color: Theme.of(context).primaryColor,
padding: const EdgeInsets.all(16),
shape: const CircleBorder(),
child: const Icon(
Icons.play_arrow,
),
),
],
),
Row(mainAxisAlignment: MainAxisAlignment.center, children: [
MaterialButton(
onPressed: () {
setState(() {
bpm -= 1;
});
},
color: Theme.of(context).primaryColor,
padding: const EdgeInsets.all(16),
shape: const CircleBorder(),
child: const Icon(
Icons.remove,
),
),
Slider(
value: bpm,
onChanged: (newValue) {
setState(() {
bpm = double.parse(newValue.toStringAsFixed(0));
});
},
min: 40,
max: 250),
MaterialButton(
onPressed: () {
setState(() {
bpm += 1;
});
},
color: Theme.of(context).primaryColor,
padding: const EdgeInsets.all(16),
shape: const CircleBorder(),
child: const Icon(
Icons.add,
),
),
]),
]
.map((e) => Padding(
padding: const EdgeInsets.only(top: 20, bottom: 20),
child: e))
.toList(),
)));
}
}
Any help would be appreciated!
Thanks :)