audio_service 0.10.0 copy "audio_service: ^0.10.0" to clipboard
audio_service: ^0.10.0 copied to clipboard

outdated

Flutter plugin to play audio in the background while the screen is off.

audio_service #

This plugin wraps around your existing audio code to allow it to run in the background, and allows your app to interact with headset buttons, the Android lock screen and notification, iOS control center, wearables and Android Auto. It is suitable for:

  • Music players
  • Text-to-speech readers
  • Podcast players
  • Navigators
  • More!
Feature Android iOS
background audio
headset click
Handle phonecall interruptions
start/stop/play/pause/seek/rate
fast forward/rewind
queue manipulation, skip next/prev
custom actions
custom events
notifications/control center (partial)
lock screen controls (partial)
album art
Android Auto (untested)

Migrating to 0.10.0 #

audio_service 0.10.0 requires a different AndroidManifest.xml configuration for headset button clicks to continue to work on Android. Your previous broadcast receiver declaration should be replaced with the one below:

    <receiver android:name="com.ryanheise.audioservice.MediaButtonReceiver" >
      <intent-filter>
        <action android:name="android.intent.action.MEDIA_BUTTON" />
      </intent-filter>
    </receiver> 

How does this plugin work? #

audio_service creates a special container for your audio code to run in that can survive the absence or destruction of your app's UI. You will therefore need to write your code in such a way that your UI code is kept separate from your audio playing code.

Within this container, you are able to write normal Dart code using your favourite audio plugins such as just_audio, flutter_radio, flutter_tts, and others, to play the actual audio. This gives you a lot of flexibility in the audio you are able to play. For example, you can play music with just_audio, or you can play text-to-speech with flutter_tts, or you could write your own Dart logic to play some combination of these all under the one container.

Note that this plugin will not work with other audio plugins that overlap in responsibility with this plugin (i.e. background audio, iOS control center, Android notifications, lock screen, headset buttons, etc.)

If you'd like to help with any missing features, join us on the GitHub issues page.

Documentation #

Example #

When using this plugin, your user interface code will run in the main UI isolate, and your audio playing code will run in a separate background isolate, enabling it to outlive the potential suspension or destruction of the UI. These two isolates do not share memory and communicate through a set of message passing APIs. To cater for this code separation, the plugin provides two sets of APIs: one for your main UI isolate (AudioService), and one for your background audio isolate (AudioServiceBackground).

UI code #

Insert an AudioServiceWidget at the top of your widget tree to maintain a connection to AudioService shared by all of your app's routes:

return MaterialApp(
  home: AudioServiceWidget(MainScreen()),
);

Once connected, your Flutter UI can start up and shut down the background audio task, and send messages to it:

AudioService.start(backgroundTaskEntrypoint: _backgroundTaskEntrypoint, params: {...});
AudioService.pause();
AudioService.play();
AudioService.skipToNext();
AudioService.skipToPrevious();
AudioService.seekTo(Duration(seconds: 10));
AudioService.stop(); // shuts down the background audio task

Your Flutter UI can listen to any changes in the state of audio playback via these streams:

AudioService.playbackStateStream    // playback state and position
AudioService.currentMediaItemStream // current item being played
AudioService.queueStream            // (optional) playlist

Consider the Flutter widget StreamBuilder to display data from the stream so that it automatically updates your UI as new events come through.

If the user closes your Flutter UI and then re-opens it, the connection to your background audio task will be automatically reestablished, and these streams will re-emit the most recent event allowing your UI to restore itself to the current state.

Background code #

The _backgroundTaskEntrypoint function that you passed into AudioService.start must be a top-level function, and it will be the first function to be called as soon as the background isolate is started. It should contain a single line of code that creates your background audio task:

void myBackgroundTaskEntrypoint() {
  AudioServiceBackground.run(() => MyBackgroundTask());
}

class MyBackgroundTask extends BackgroundAudioTask {
  AudioPlayer _audioPlayer = AudioPlayer();
  Completer _completer = Completer();
  
  @override
  Future<void> onStart(Map<String, dynamic> params) async {
    // Your custom dart code to start audio playback.
    // NOTE: The background audio task will shut down
    // as soon as this async function completes.
    return _completer.future;
  }
  @override
  void onStop() {
    // Your custom dart code to stop audio playback. e.g.:
    _audioPlayer.stop();
    // Cause the audio task to shut down.
    _completer.complete();
  }
  @override
  void onPlay() {
    // Your custom dart code to resume audio playback. e.g.:
    _audioPlayer.play();
    // Broadcast the state change to all user interfaces:
    AudioServiceBackground.setState(basicState: BasicPlaybackState.playing, ...);
  }
  @override
  void onPause() {
    // Your custom dart code to pause audio playback. e.g.:
    _audioPlayer.pause();
    // Broadcast the state change to all user interfaces:
    AudioServiceBackground.setState(basicState: BasicPlaybackState.paused, ...);
  }
  @override
  void onClick(MediaButton button) {
    // Your custom dart code to handle a click on a headset.
  }
  @override
  void onSkipToNext() {
    // Your custom dart code to skip to the next queue item.
  }
  @override
  void onSkipToPrevious() {
    // Your custom dart code to skip to the previous queue item.
  }
  @override
  void onSeekTo(Duration position) {
    // Your custom dart code to seek to a position.
  }
  @override
  void onAudioFocusLost(AudioInterruption interruption) {
    // Your custom dart code to handle a phone call or other interruption.
  }
  @override
  void onAudioFocusGained(AudioInterruption interruption) {
    // Your custom dart code to handle the end of an audio interruption.
  }
}

These callbacks get called not only in response to method calls from your UI (like AudioService.play) but also when the user clicks on a button in your Android notification, lock screen, iOS control center, or headphone buttons (in the case of onClick).

At a bare minimum, you must override the onStart and onStop callbacks to manage setting up and tearing down the background audio task, while all other callbacks are optional depending on your app's requirements.

During the operation of your background audio task, you should broadcast any state changes to all user interfaces using these methods:

// Tell all UIs the playback state has changed (playing/paused/...)
AudioServiceBackground.setState
// Tell all UIs we're now playing a particular item (title/artist/image/...)
AudioServiceBackground.setMediaItem
// Tell all UIs the queue/playlist has changed
AudioServiceBackground.setQueue

This allows not only your Flutter UI, but also the Android notification, iOS command center, etc. to update the information they display to the user.

A full example is provided on GitHub demonstrating both music and text-to-speech use cases.

Android setup #

These instructions assume that your project follows the new project template introduced in Flutter 1.12. If your project was created prior to 1.12 and uses the old project structure, you can update your project to follow the new project template.

Additionally:

  1. Edit your project's AndroidManifest.xml file to declare the permission to create a wake lock, and add component entries for the <service> and <receiver>:
<manifest ...>
  <uses-permission android:name="android.permission.WAKE_LOCK"/>
  <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
  
  <application ...>
    
    ...
    
    <service android:name="com.ryanheise.audioservice.AudioService">
      <intent-filter>
        <action android:name="android.media.browse.MediaBrowserService" />
      </intent-filter>
    </service>

    <receiver android:name="com.ryanheise.audioservice.MediaButtonReceiver" >
      <intent-filter>
        <action android:name="android.intent.action.MEDIA_BUTTON" />
      </intent-filter>
    </receiver> 
  </application>
</manifest>
  1. Any icons that you want to appear in the notification (see the MediaControl class) should be defined as Android resources in android/app/src/main/res. Here you will find a subdirectory for each different resolution:
drawable-hdpi
drawable-mdpi
drawable-xhdpi
drawable-xxhdpi
drawable-xxxhdpi

You can use Android Asset Studio to generate these different subdirectories for any standard material design icon.

Starting from Flutter 1.12, you will also need to disable the shrinkResources setting in your android/app/build.gradle file, otherwise your icon resources will be removed during the build:

android {
    compileSdkVersion 28

    ...

    buildTypes {
        release {
            signingConfig ...
            shrinkResources false // ADD THIS LINE
        }
    }
}

iOS setup #

Insert this in your Info.plist file:

	<key>UIBackgroundModes</key>
	<array>
		<string>audio</string>
	</array>

The example project may be consulted for context.

1114
likes
0
pub points
98%
popularity

Publisher

verified publisherryanheise.com

Flutter plugin to play audio in the background while the screen is off.

Repository (GitHub)
View/report issues

License

unknown (LICENSE)

Dependencies

flutter, flutter_cache_manager, flutter_isolate, rxdart

More

Packages that depend on audio_service