---
title: Audio Context
description: How to manage audio context and interruptions
---

`flutter_soloud` handles the playing of audio, but does not handle configuration of the OS level audio sessions and their contexts. If you wish to change the audio session away from OS defaults (for example, to always play over other apps or ignore silent mode), we recommend [audio_session](https://pub.dev/packages/audio_session).

This page provides a guide on how to use `audio_session` to manage the audio context.

<Info>
Please, look at the `lib/audio_context/audio_context.dart` example for a simple implementation to handle audio interruptions and the use of `audio_service` also for background audio.
</Info>

## Setup

First, add `audio_session` to your `pubspec.yaml`:

```yaml
dependencies:
  audio_session: ^0.2.2
```

## Basic Usage

Initialize and configure `audio_session` in your app. A good place to do this is in the `initState` of your widget and should be done before any audio is played.

```dart
import 'package:audio_session/audio_session.dart';

// ...

late final AudioSession session;

@override
void initState() {
  super.initState();
  AudioSession.instance.then((audioSession) async {
    session = audioSession;
    await session.configure(
      const AudioSessionConfiguration(
        androidWillPauseWhenDucked: true,
        androidAudioAttributes: AndroidAudioAttributes(
          usage: AndroidAudioUsage.media,
          contentType: AndroidAudioContentType.music,
        ),
        androidAudioFocusGainType: AndroidAudioFocusGainType.gainTransientMayDuck,
        avAudioSessionCategory: AVAudioSessionCategory.playback,
        avAudioSessionCategoryOptions: AVAudioSessionCategoryOptions.none,
      ),
    );
    _handleInterruptions(session);
  });
}
```

When you want to start to play audio, you need to activate the audio session:

```dart
await session.setActive(true);
final handle = await SoLoud.instance.play(sound);
```

## Handling Interruptions

You need to listen to interruption events to pause, resume, or duck your audio.

```dart
void _handleInterruptions(AudioSession audioSession) {
  audioSession.becomingNoisyEventStream.listen((_) {
    // The user unplugged headphones, so we should pause.
    SoLoud.instance.setPause(soundHandle, true);
  });

  audioSession.interruptionEventStream.listen((event) {
    if (event.begin) {
      switch (event.type) {
        case AudioInterruptionType.duck:
          // Another app started playing audio and we should duck.
          SoLoud.instance.fadeGlobalVolume(0.1, const Duration(milliseconds: 300));
          break;
        case AudioInterruptionType.pause:
        case AudioInterruptionType.unknown:
          // Another app started playing audio and we should pause.
          SoLoud.instance.setPause(soundHandle, true);
          break;
      }
    } else {
      switch (event.type) {
        case AudioInterruptionType.duck:
          // The interruption ended and we should unduck.
          SoLoud.instance.fadeGlobalVolume(1, const Duration(milliseconds: 300));
          break;
        case AudioInterruptionType.pause:
          // The interruption ended and we should resume.
          SoLoud.instance.setPause(soundHandle, false);
          break;
        case AudioInterruptionType.unknown:
          // The interruption ended but we should not resume.
          break;
      }
    }
  });
}
```

## Output-Device Lifecycle

Voice state and output-device state are separate. Pausing a voice preserves its `SoundHandle` and its SoLoud state. Device lifecycle operations only stop, start, or prewarm the platform audio output; they do not maintain a second copy of voice volume, pan, speed, fades, looping, seek position, or other properties.

`play()` and `play3d()` are synchronous and return a `SoundHandle` immediately. An unpaused voice starts the output device after the voice has been created successfully. Creating a paused voice does not start the device; unpausing it later does.

When no unpaused voices remain, the output device follows the configured idle timeout:

- `setAudioDeviceIdleTimeout(Duration.zero)` stops it as soon as possible.
- A positive duration keeps it running for that grace period.
- `setAudioDeviceIdleTimeout(null)` keeps it running indefinitely, including across `deinit()` and a later `init()`.

`startAudioDevice()` temporarily starts or prewarms the output and completes after startup finishes. It does not enable permanent keep-alive; if the engine is still idle, the configured timeout begins again. `stopAudioDevice()` is an idle-only conditional stop by default, so it succeeds without interrupting active playback. Use `stopAudioDevice(force: true)` only when the output must be stopped while voices remain active; their voice state is not changed.

`getAudioDeviceState()` is a cheap synchronous read of the actual backend state: `uninitialized`, `stopped`, `started`, `starting`, or `stopping`. It does not report a pending scheduler request.

<Info>
  **iOS and Google Ads**

  If you are using the `google_mobile_ads` plugin on iOS, you might encounter issues with audio playback. To solve this, you can configure the `audio_session` adding this line in `AppDelegate.swift`:

  ```dart
import GoogleMobileAds

func application(_ application: UIApplication,
                 didFinishLaunchingWithOptions launchOptions: 
                 [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
 
  // Add this
  MobileAds.shared.audioVideoManager.isAudioSessionApplicationManaged = true

...
 return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
  ```
</Info>
