Search code examples
flutteraudiojust-audioaudio-service

Flutter background audio with progressbar


I want to play audio from a stream. I have used Just Audio for this. I want a progress bar like Audio Video Progress bar also play the audio in the background using Audio Service.

I have used the examples for all of them and they are working individually but I cannot concatenate them. would you please help with an example of how can I do it?


Solution

  • The example from audio_service plugin contains how to use audio_service along with just_audio. (combining 2 packages done)

    Below is the mentioned SeekBar given in the example of the audio_service plugin.

    // A seek bar.
    StreamBuilder<MediaState>(
        stream: _mediaStateStream,
        builder: (context, snapshot) {
            final mediaState = snapshot.data;
            return SeekBar(
                duration:
                    mediaState?.mediaItem?.duration ?? Duration.zero,
                position: mediaState?.position ?? Duration.zero,
                onChangeEnd: (newPosition) {
                    AudioService.seekTo(newPosition);
                },
            );
        },
    ),
    ...
    
    class SeekBar extends StatefulWidget {
      final Duration duration;
      final Duration position;
      final ValueChanged<Duration>? onChanged;
      final ValueChanged<Duration>? onChangeEnd;
    
      SeekBar({
        required this.duration,
        required this.position,
        this.onChanged,
        this.onChangeEnd,
      });
    
      @override
      _SeekBarState createState() => _SeekBarState();
    }
    
    // more code in the plugin example
    

    Now from the above code, as you can see the example uses custom-designed SeekBar (which uses slider).

    Instead of the custom slider, you can possibly use the plugin widget you wish. ProgressBar in your case.

    StreamBuilder<MediaState>(
        stream: _mediaStateStream,
        builder: (context, snapshot) {
            final mediaState = snapshot.data;
            return ProgressBar(
                total:
                    mediaState?.mediaItem?.duration ?? Duration.zero,
                progress: mediaState?.position ?? Duration.zero,
                onSeek: (newPosition) {
                    AudioService.seekTo(newPosition);
                },
            );
        },
    ),
    

    This is a change for the above SeekBar with ProgressBar. (Combining 3rd package done)

    Note: For the buffered. You can get the bufferedPosition from the audio_service PlaybackStateStream.

    Edit: The below is how I used it to add bufferedPosition.

    (I changed the MediaState class, corresponding state getter a bit to achieve it)

    StreamBuilder<MediaState>(
        stream: _mediaStateStream,
        builder: (context, snapshot) {
            final mediaState = snapshot.data;
            return ProgressBar(
                total:
                    mediaState?.mediaItem?.duration ?? Duration.zero,
                progress: mediaState?.position ?? Duration.zero,
                buffered: mediaState?.playbackState?
                    .bufferedPosition ?? Duration.zero,
                onSeek: (newPosition) {
                    AudioService.seekTo(newPosition);
                },
            );
        },
    ),
    ...
    Stream<MediaState> get _mediaStateStream =>
      Rx.combineLatest3<MediaItem, Duration, PlaybackState, MediaState>(
          AudioService.currentMediaItemStream,
          AudioService.positionStream,
          AudioService.playbackStateStream,
          (mediaItem, position, playbackState) =>
              MediaState(mediaItem, position, playbackState));
    ...
    class MediaState {
      final MediaItem mediaItem;
      final Duration position;
      final PlaybackState playbackState;
    
      MediaState(this.mediaItem, this.position, this.playbackState);
    }