I'm trying to make a basic audio player in flutter and while the program works perfectly fine after a hot restart, when I initially boot up the app the slider progress bar gets reset to 0 every time I hit play, even though I have statements in the code to seek to the current time. Playing around with my print statements showed me that up until the moment and including after I use player.resume(), my currentTime variable stores the correct progress, only changing back to 0 once the player resumes and my logic for updating the currentTime while player is playing kicks in. This tells me that my seek() statements aren't going through until after a hot restart.
This is the code:
import 'package:pick_pro/main.dart';
import 'package:pick_pro/metronome.dart';
import 'package:audioplayers/audioplayers.dart';
const Color darkBlue = Color(0xFF000c24);
const Color blueGreen = Color.fromARGB(255, 121, 207, 175);
final TextEditingController _controller = TextEditingController();
final TextEditingController _speedController = TextEditingController();
final TextEditingController _hintController = TextEditingController();
class Playback extends StatefulWidget {
@override
PlaybackState createState() => PlaybackState();
}
class PlaybackState extends State<Playback> {
final player = AudioPlayer();
late Duration songLength;
Duration currentTime = Duration.zero;
double playbackSpeed = 1;
String assetPath = 'assets/sounds/demo_song.mp3';
bool isPlaying = false;
int bpm = 100;
@override
void initState() {
super.initState();
player.setReleaseMode(ReleaseMode.LOOP);
// Set defualt song
player.setUrl(assetPath, isLocal: true);
songLength = const Duration(seconds: 259);
// Listeners for changes in player states
player.onPlayerStateChanged.listen((state) {
setState(() {
isPlaying = state == PlayerState.PLAYING;
});
});
player.onDurationChanged.listen((newSongLength) {
setState(() {
songLength = newSongLength;
});
});
player.onAudioPositionChanged.listen((newCurrentTime) {
setState(() {
if (isPlaying) {
currentTime = newCurrentTime;
}
});
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text(
'PickPro',
style: TextStyle(
fontSize: 40.0,
fontFamily: 'Caveat',
),
),
centerTitle: true,
backgroundColor: blueGreen,
),
body: Container(
color: darkBlue,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const SizedBox(
height: 32,
),
Text('Get Song Name from metadata', style: buttonText()),
const SizedBox(height: 20),
Slider(
min: 0,
max: songLength.inSeconds.toDouble(),
value: currentTime.inSeconds.toDouble(),
onChanged: (value) {
currentTime = Duration(seconds: value.toInt());
setState(() {
player.pause();
player.seek(currentTime);
});
}),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
formatTime(currentTime),
style: buttonText(),
),
Text(
formatTime(songLength),
style: buttonText(),
),
],
),
),
const SizedBox(height: 20),
IconButton(
icon: Icon(isPlaying ? Icons.pause : Icons.play_arrow,
color: Colors.white),
iconSize: 50,
onPressed: () {
if (isPlaying) {
setState(() {
player.pause();
});
} else {
setState(() {
player.resume();
print(formatTime(currentTime));
});
}
},
),
const SizedBox(height: 20),
Slider(
min: 0.5,
max: 1.5,
value: playbackSpeed,
onChanged: (value) {
playbackSpeed = double.parse(value.toStringAsFixed(2));
player.pause();
player.setPlaybackRate(playbackSpeed);
setState(() {});
}),
Center(
child: Text(
'Speed: ${playbackSpeed}x',
style: buttonText(),
),
),
const SizedBox(
height: 30,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
SizedBox(
width: 120,
height: 30,
child: TextField(
textAlign: TextAlign.center,
controller: _hintController,
enabled: false,
decoration: const InputDecoration(
hintText: 'Song BPM:',
hintStyle:
TextStyle(color: blueGreen, fontSize: 24),
filled: true,
fillColor: Colors.transparent,
border: InputBorder.none,
enabledBorder: InputBorder.none,
focusedBorder: InputBorder.none,
contentPadding: EdgeInsets.zero),
cursorColor: Colors.white,
onSubmitted: (String value) {})),
SizedBox(
width: 60.0,
height: 30.0,
child: TextField(
textAlign: TextAlign.center,
controller: _controller,
keyboardType: TextInputType.number,
style: const TextStyle(
color: blueGreen,
fontSize: 24.0,
),
decoration: InputDecoration(
hintText: '${bpm}',
hintStyle:
const TextStyle(color: blueGreen, fontSize: 24),
alignLabelWithHint: true,
filled: true,
fillColor: Colors.transparent,
border: InputBorder.none,
enabledBorder: InputBorder.none,
focusedBorder: InputBorder.none,
contentPadding: EdgeInsets.zero),
cursorColor: Colors.white,
onSubmitted: (String value) {
_controller.clear();
try {
bpm = int.parse(value);
setState(() {});
} catch (e) {
ErrorPopup.show(
context, "The BPM you entered is invalid.");
}
},
),
),
],
),
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
SizedBox(
width: 145,
height: 30,
child: TextField(
textAlign: TextAlign.center,
controller: _hintController,
enabled: false,
decoration: const InputDecoration(
hintText: 'Desired BPM:',
hintStyle:
TextStyle(color: blueGreen, fontSize: 24),
filled: true,
fillColor: Colors.transparent,
border: InputBorder.none,
enabledBorder: InputBorder.none,
focusedBorder: InputBorder.none,
contentPadding: EdgeInsets.zero),
cursorColor: Colors.white,
onSubmitted: (String value) {})),
SizedBox(
width: 60.0,
height: 30.0,
child: TextField(
textAlign: TextAlign.center,
controller: _speedController,
keyboardType: TextInputType.number,
style: const TextStyle(
color: blueGreen,
fontSize: 24.0,
),
decoration: InputDecoration(
hintText: '${bpm * playbackSpeed}',
hintStyle:
TextStyle(color: blueGreen, fontSize: 24),
alignLabelWithHint: true,
filled: true,
fillColor: Colors.transparent,
border: InputBorder.none,
enabledBorder: InputBorder.none,
focusedBorder: InputBorder.none,
contentPadding: EdgeInsets.zero),
cursorColor: Colors.white,
onSubmitted: (String value) {
_controller.clear();
try {
int newBPM = int.parse(value);
playbackSpeed =
double.parse((newBPM / bpm).toStringAsFixed(2));
player.pause();
player.setPlaybackRate(playbackSpeed);
setState(() {});
} catch (e) {
ErrorPopup.show(
context, "The BPM you entered is invalid.");
}
},
),
),
],
),
],
),
],
),
),
drawer: MyDrawer(index: 3),
);
}
String formatTime(Duration duration) {
// Format single integers to 2 digits long (i.e. 02)
String formatDigits(int n) => n.toString().padLeft(2, '0');
final hours = formatDigits(duration.inHours);
final minutes = formatDigits(duration.inMinutes);
final seconds = formatDigits(
duration.inSeconds - 60 * (duration.inSeconds / 60).floor());
List<String> time = [];
if (duration.inHours > 0) {
time.add(hours);
}
time.add(minutes);
time.add(seconds);
return time.join(':');
}
}
I don't know what to try as seek() is built into the audioplayers plugin. It works after a hot restart, I just don't know if it'll work on iOS or Android.