createPositionStream method

Stream<Duration> createPositionStream(
  1. {int steps = 800,
  2. Duration minPeriod = const Duration(milliseconds: 200),
  3. Duration maxPeriod = const Duration(milliseconds: 200)}
)

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();
}