Skip to content
ryanheise edited this page Sep 11, 2021 · 32 revisions

Frequently Asked Questions

Note: Some questions may be specific to version 0.17 or 0.18. These will be labelled as such.

I think I found a bug! How can I report it?

First, it is a good idea to search through past GitHub issues to ensure that you are not posting a duplicate issue.

Second, it is a good idea to search through this FAQ, since some errors are not necessarily bugs, and can be avoided.

Finally, click on the Issues page and click the button to create a new bug report. Follow the instructions carefully and supply all requested information, leaving the section headings intact, but filling in the body of each section. Please do not delete or skip these sections as they are necessary in order to investigate a bug.

Why aren't my notification buttons and headset buttons working?

On Android, such button clicks are handled by a broadcast receiver which must be declared in your AndroidManifest.xml file:

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

Why does the control center not appear on iOS?

This can result from a conflict with the flutter_tts plugin, which is also used in the example. (See the open issue #202)

It can also result from your AVAudioSession settings. Use the audio_session package to configure appropriate AVAudioSession settings for your app. E.g.:

audioSession.configure(AudioSessionConfiguration.speech());
// or...
audioSession.configure(AudioSessionConfiguration.music());
// or a custom configuration:
audioSession.configure(...);

(Note: The default preset AudioSessionConfiguration.music() in audio_session 0.0.4 and earlier had some options set in it that were not appropriate for showing a notification. This is fixed in version 0.0.5 and later.)

Can I connect to my audio handler from background code?

Certain Plugins like android_alarm_manager let your app "wake up" from the background and execute Dart code. The callbacks for these plugins typically execute in a separate isolate, and so to communicate with your audio handler running in the main isolate you must use inter-isolate communication. To do this, follow the README example for inter-isolate communication:

// Wrap audio handler in IsolatedAudioHandler:
_audioHandler = await AudioService.init(
  builder: () => IsolatedAudioHandler(
    MyAudioHandler(),
    portName: 'my_audio_handler',
  ),
);
// From another isolate, obtain a proxy reference:
_proxyAudioHandler = await IsolatedAudioHandler.lookup(
  portName: 'my_audio_handler',
);

Now you can communicate with your handler via _proxyAudioHandler.

Can this plugin be used with audioplayers

audioplayers has both worked and not worked at different times in the past. If it currently does not work, you can contact the author of that plugin and make a feature request.

Relevant information: For an audio player plugin to work with audio_service there needs to be clearly delineated roles between the two plugins such that only audio_service controls the various APIs involved in:

  1. Enabling background execution
  2. Displaying notifications, lock screen controls, Control Center info, etc.
  3. Configuring the audio session
  4. Handling remote controls from headsets, smart watches, car stereos, etc.
  5. Handling audio focus events (e.g. if another app takes over audio focus temporarily)

If the other plugin does any of these things, the two plugins will overlap in responsibility and things will probably not work. In other words, you will need an audio player plugin that just plays audio, and doesn't take on any of these other responsibilities listed above.

If at present audioplayers doesn't work for you, you can alternatively try my plugin just_audio which was designed with this delineation in mind.

Can I pause audio when receiving an incoming call?

Yes. Some audio players may provide an option to do this for you. If not, you can use the audio_session library to listen to audio interruption events and play/pause as appropriate:

session.interruptionEventStream.listen((event) {
  // play/pause/duck audio depending on the event
});

See the audio_session package for further information.

Is it possible to make the headphone (media) buttons work in a text-to-speech app?

There is an issue on Android, but there is a workaround. Android requires an active media session to route media button events to, and actual audio needs to be played to activate a media session (text-to-speech does not qualify). One trick is to play a short segment of silent audio to activate the session. You can use AudioService.androidForceEnableMediaButtons() to achieve this.

How do I send a custom action?

If you want to send a message from the UI to your background audio task (0.17) or audio handler (0.18) but it is not one of the predefined messages defined in AudioService, you can send the message as a custom action. E.g.

// In your UI
await _audioHandler.customAction('setVolume', { 'value': 0.5 });

And then in your background audio task or audio handler, implement the corresponding callback:

@override
Future<dynamic> customAction(String name, [Map<String, dynamic>? extras]) async {
    if (name == 'setVolume') {
        _audioPlayer.setVolume(extras['value']);
    }
}

Note that the arguments parameter and the map values of the extras parameter must be any data structure that can be encoded by the standard message codec. If you return a value from this callback, it will also be returned back to the caller of customAction.

How do I send a custom event?

If you want to send an event from your background audio task to the Flutter UI but it is not one of the predefined messages, you can send the message as a custom event. E.g.

// In your audio handler
customEvent.add("itHappened");

And then in your Flutter UI:

_audioHandler.customEvent.listen((event) {
  print("Received event: $event");
}

How do I update an existing MediaItem?

You cannot update fields in a MediaItem object since it is immutable, although in the same style as ThemeData.copyWith, you can use MediaItem.copyWith to create a copy with some fields modified. e.g.

modifiedMediaItem = mediaItem.copyWith(duration: duration);

If you are updating the current media item in your audio handler, remember to broadcast this change to all clients:

mediaItem.add(modifiedMediaItem);

If you are updating a media item in the queue in your audio handler, remember to broadcast your modified queue to all clients:

myQueue[pos] = modifiedMediaItem;
queue.add(myQueue);

How can I stop the audio handler when the user swipes away the app in the task manager?

For Android, you can override the onTaskRemoved callback to control what happens when the user swipes away the task in the task manager. By default, the operating system will just clear your app's activity (UI) from memory leaving your service to run in the background with the audio. But you can override this callback to cause your service to also stop. In your background audio task or audio handler add:

  Future<void> onTaskRemoved() async {
    await stop();
  }

If you use the androidStopForegroundOnPause option, when your audio is paused, the operating system moves your service to a lower priority level where it can be destroyed at any time to reclaim memory. If the user swipes away your task under these conditions, the operating system will destroy your service, and you may override this method to do any cleanup. For example:

  Future<void> onTaskRemoved() async {
    if (!playbackState.value.playing) {
      await stop();
    }
  }

Why is the Android seek bar black on some devices?

On some Android devices, the seek bar will only render correctly if the androidNotificationIcon option points to a monochrome white icon on a transparent background, and the notificationColor is set to a non-transparent color.

How do I test the latest code on git?

Sometimes you may want to try out the latest code on git master, e.g. to try out a bug fix or new feature that hasn't been released yet. To do so, you can temporarily change your pubspec.yaml file to:

dependencies:
  audio_service:
    git:
      url: https://github.com/ryanheise/audio_service.git
      path: audio_service

Note that the latest git code is not necessarily stable and should not be used in production.