createPositionStream method
Creates a new stream periodically tracking the current position of this
player. The stream will aim to emit steps
position updates from the
beginning to the end of the current audio source, at intervals of
duration / steps
. This interval will be clipped between minPeriod
and maxPeriod
. This stream will not emit values while audio playback is
paused or stalled.
Note: each time this method is called, a new stream is created. If you intend to use this stream multiple times, you should hold a reference to the returned stream and close it once you are done.
Implementation
Stream<Duration> createPositionStream({
int steps = 800,
Duration minPeriod = const Duration(milliseconds: 200),
Duration maxPeriod = const Duration(milliseconds: 200),
}) {
assert(minPeriod <= maxPeriod);
assert(minPeriod > Duration.zero);
final controller = StreamController<Duration>.broadcast();
if (_disposed) return controller.stream;
Duration duration() => this.duration ?? Duration.zero;
Duration step() {
var s = duration() ~/ steps;
if (s < minPeriod) s = minPeriod;
if (s > maxPeriod) s = maxPeriod;
return s;
}
Timer? currentTimer;
StreamSubscription<Duration?>? durationSubscription;
StreamSubscription<PlaybackEvent>? playbackEventSubscription;
void yieldPosition(Timer timer) {
if (controller.isClosed) {
timer.cancel();
durationSubscription?.cancel();
playbackEventSubscription?.cancel();
return;
}
if (_durationSubject.isClosed) {
timer.cancel();
durationSubscription?.cancel();
playbackEventSubscription?.cancel();
// This will in turn close _positionSubject.
controller.close();
return;
}
if (playing) {
controller.add(position);
}
}
durationSubscription = durationStream.listen((duration) {
currentTimer?.cancel();
currentTimer = Timer.periodic(step(), yieldPosition);
}, onError: (Object e, StackTrace stackTrace) {});
playbackEventSubscription = playbackEventStream.listen((event) {
controller.add(position);
}, onError: (Object e, StackTrace stackTrace) {});
return controller.stream.distinct();
}