# Add voice guidance

Last updated:

<!-- -->

October 18, 2026

<!-- -->

\| 8 minutes read

<br />

<br />

Enhance navigation experiences with spoken instructions. This guide covers enabling built-in Text-to-Speech (TTS), managing voice settings, switching voices and languages, and integrating custom playback.

## What you need[​](#what-you-need "Direct link to What you need")

The Maps SDK for Flutter provides two options for instruction playback:

* **Built-in solutions** - playback using human voice recordings or computer-generated TTS
* **External integration** - delivery of TTS strings for use with third-party packages

The built-in solution provides automatic audio session management, ducking other playbacks (such as music) while instructions play.

## Step 1: Enable voice guidance[​](#step-1-enable-voice-guidance "Direct link to Step 1: Enable voice guidance")

Set the `canPlaySounds` flag of the `SoundPlayingService` to true:

```dart
SoundPlayingService.canPlaySounds = true;

```

Use the `canPlaySounds` getter and setter to check or change whether the SDK plays instructions during navigation or simulation.

warning

Ensure a valid TTS voice is configured, voice volume is set to a positive value, and the `canPlaySounds` flag is enabled to automatically play voice instructions.

Tip

By default, the current voice is set to the best computer TTS voice matching the default SDK language.

**Limitations:**<br /><!-- -->Customizing the timing of TTS instructions is not supported. Filtering TTS instructions based on custom logic is not available.

## Step 2: Configure the Sound Playing Service[​](#step-2-configure-the-sound-playing-service "Direct link to Step 2: Configure the Sound Playing Service")

The `SoundPlayingService` manages voice playback with the following features:

| Member                            | Description                                                        |
| --------------------------------- | ------------------------------------------------------------------ |
| `playText(text)`                  | Plays the given TTS text string (computer voices only) (async)     |
| `voiceVolume`                     | Gets or sets volume level for voice playback (0-10)                |
| `canPlaySounds`                   | Gets or sets whether automatic TTS instruction playback is enabled |
| `cancelNavigationSoundsPlaying()` | Cancels ongoing navigation-related sound playback                  |
| `soundPlayingListener`            | Sound listener providing details about sound playing events        |

## Step 3: Select and configure voices[​](#step-3-select-and-configure-voices "Direct link to Step 3: Select and configure voices")

The SDK provides voices for each supported language. Download and activate voices to deliver navigation prompts such as turn instructions, warnings, and announcements.

### Voice types[​](#voice-types "Direct link to Voice types")

The SDK offers two types of voice guidance:

* **`VoiceType.human`** - Pre-recorded human voices delivering instructions in a natural tone. Supports basic instruction types only; **does not** include road or settlement names.
* **`VoiceType.computer`** - Device Text-to-Speech (TTS) engine providing detailed, flexible guidance. **Fully supports** street and place names. Quality depends on device capabilities.

### Voice structure[​](#voice-structure "Direct link to Voice structure")

The `Voice` class contains:

| Property   | Type      | Description                                                        |
| ---------- | --------- | ------------------------------------------------------------------ |
| `id`       | int       | Unique identifier for the voice                                    |
| `name`     | String    | Display name of the voice                                          |
| `fileName` | String    | File name used to load the voice (available for `VoiceType.human`) |
| `language` | Language  | Associated language object                                         |
| `type`     | VoiceType | Voice type: `human` or `computer`                                  |

danger

⚠️ **Do not confuse `Voice` and `Language` concepts**

* **`Language`** defines **what** is said - words, phrasing, and localization
* **`Voice`** defines **how** it is said - accent, tone, and gender

Ensure the selected `Voice` is compatible with the chosen `Language`. Mismatched combinations may result in unnatural or incorrect pronunciation.

**Relevance:**

* `Language` - relevant for built-in TTS and custom solutions using `onTextToSpeechInstruction`. See the [internationalization guide](/docs/flutter/guides/get-started/internationalization.md).
* `Voice` - relevant **only** for built-in voice-guidance (human and computer voices).

danger

⚠️ **Two language settings**

The SDK distinguishes between:

* **SDK language** (`SdkSettings.language`) - language for on-screen text and UI strings
* **Voice language** (`Voice.language`) - language for spoken output (built-in engine or `onTextToSpeechInstruction` callback)

Both use the same `Language` class. Synchronize SDK language and voice language based on your use case.

### Get the current voice[​](#get-the-current-voice "Direct link to Get the current voice")

Use the `voice` getter from `SdkSettings`:

```dart
Voice currentVoice = SdkSettings.voice;

```

### Get available human voices[​](#get-available-human-voices "Direct link to Get available human voices")

Retrieve the available human voices list using `getLocalContentList` from the `ContentStore` class. Voice details are stored in the `contentParameters` field of each `ContentStoreItem`. Use `contentParametersAs` getter to safely cast to `VoiceParameters`.

```dart
List<ContentStoreItem> items = ContentStore.getLocalContentList(ContentType.humanVoice);

for (final contentStoreItem in items) {
  final String name = contentStoreItem.name;
  final String filePath = contentStoreItem.fileName;
  
  final VoiceParameters? voiceContentParameters =
      contentStoreItem.contentParametersAs<VoiceParameters>();
  
  if (voiceContentParameters == null) {
    print("Content parameters are null for voice: $name");
    continue;
  }
  
  final String? languageCode = voiceContentParameters.language;
  final String? gender = voiceContentParameters.gender;
  final VoiceType? type = voiceContentParameters.type;
  final String? nativeLanguage = voiceContentParameters.nativeLanguage;
}

```

Tip

See the [Manage Content Guide](/docs/flutter/guides/offline/manage-content.md) for downloading, deleting, and managing voices, plus details about `ContentStore` and `ContentStoreItem`.

### Apply a voice by path[​](#apply-a-voice-by-path "Direct link to Apply a voice by path")

Provide the absolute path (from `ContentStoreItem.fileName`) to `setVoiceByPath`:

```dart
String filePath = contentStoreItem.fileName;
GemError error = SdkSettings.setVoiceByPath(filePath);

if (error != GemError.success) {
  print("Applying the voice failed.");
}

```

Tip

`setVoiceByPath` also supports computer voices if the path is known. Specify the `language` optional parameter with a `Language` instance when setting computer voices to ensure proper matching. The `language` parameter is only relevant for computer voices.

### Apply a voice by language[​](#apply-a-voice-by-language "Direct link to Apply a voice by language")

Apply computer voices using `setTTSVoiceByLanguage` from the `SdkSettings` class:

```dart
Language? lang = SdkSettings.getBestLanguageMatch("eng", regionCode: "GBR");
GemError error = SdkSettings.setTTSVoiceByLanguage(lang!);

```

Computer voices use the device's built-in TTS capabilities.

warning

Selecting a computer voice in an unsupported language may cause a mismatch between spoken voice and instruction content. Exact behavior depends on device TTS capabilities.

## Step 4: Integrate external TTS (optional)[​](#step-4-integrate-external-tts-optional "Direct link to Step 4: Integrate external TTS (optional)")

The `NavigationService` provides TTS instructions as strings for processing with external tools. Use the [flutter\_tts](https://pub.dev/packages/flutter_tts) package or other TTS solutions to play instructions.

Add `flutter_tts` to your `pubspec.yaml` and run `dart pub get`.

Use the `onTextToSpeechInstruction` callback during simulation:

```dart
// instantiate FlutterTts
FlutterTts flutterTts = FlutterTts();

void simulationInstructionUpdated(NavigationInstruction instruction, Set<NavigationInstructionUpdateEvents> events) {
  // handle instruction
}

void textToSpeechInstructionUpdated(String ttsInstruction) {
  flutterTts.speak(ttsInstruction);
}

TaskHandler? taskHandler = NavigationService.startSimulation(
  route,
  onNavigationInstruction: simulationInstructionUpdated,
  onTextToSpeechInstruction: textToSpeechInstructionUpdated,
  speedMultiplier: 2,
);

```

For navigation, set the `onTextToSpeechInstruction` callback similarly with `startNavigation`.

See [flutter\_tts](https://pub.dev/packages/flutter_tts) documentation for setting TTS voice, language, pitch, volume, speechRate, and other options.

Tip

**Change instruction language:**

* Use `setTTSVoiceByLanguage` and specify the preferred language
* Or use `setTTSVoiceByPath` with a voice path for the desired language

**Disable internal playback:**<br /><!-- -->Set `SoundPlayingService.canPlaySounds` to `false`. Instructions still arrive via the callback, but no audio plays.

## Step 5: Monitor sound playback events[​](#step-5-monitor-sound-playback-events "Direct link to Step 5: Monitor sound playback events")

The `SoundPlayingListener` class observes sound playback events, including navigation TTS instructions, custom text, audio files, and alerts.

**Registration methods:**

* `registerOnStart` - triggered when a sound starts playing
* `registerOnStop` - triggered when a sound finishes playing. Error is `GemError.success` if completed, or other errors if canceled
* `registerOnVolumeChangedByKeys` - triggered when the user adjusts volume using physical device buttons

Register callbacks using the singleton instance from `SoundPlayingService.soundPlayingListener`:

```dart
final listener = SoundPlayingService.soundPlayingListener;

listener.registerOnStart(() {
  // Sound started playing
});

listener.registerOnStop((error) {
  // Sound finished playing
});

listener.registerOnVolumeChangedByKeys((newVolume) {
  // Volume changed
});

```

**Retrieve details about currently playing sound:**

* `soundPlayType` - playback type (none, custom text, audio file, alert, navigation TTS, or custom sound by ID)
* `soundPlayContent` - playback string (only for `SoundPlayType.navigationSound` or `SoundPlayType.soundById`)
* `soundPlayFileName` - audio file name (only for `SoundPlayType.file` or `SoundPlayType.alert`)

Only one sound can play at a time. The `SoundPlayingListener` instance persists from SDK initialization to release.

## Step 6: Play custom instructions[​](#step-6-play-custom-instructions "Direct link to Step 6: Play custom instructions")

Use the `playText` method of `SoundPlayingService` to play custom instructions (e.g., road warnings or social reports). This uses the currently selected computer voice and is **not available for human voices**.

The optional `severity` parameter determines interrupt behavior:

* `information` - queued and played **after** current playback
* `warning` - **interrupts** current playback if it has lower priority

Ensure the provided string matches the voice language.

Tip

See [speed warnings](/docs/flutter/guides/alarms/speed-alarms.md) and [landmark & overlay alarms](/docs/flutter/guides/alarms/landmark-and-overlay-alarms.md) for notifications about speed warnings and reports.

## Step 7: Adjust sound settings[​](#step-7-adjust-sound-settings "Direct link to Step 7: Adjust sound settings")

Fine-tune playback behavior for **voice guidance** and **alerts** to match your app's audio experience.

### Set voice and warning volumes[​](#set-voice-and-warning-volumes "Direct link to Set voice and warning volumes")

Use `SoundPlayingService.voiceVolume` and `SoundPlayingService.warningsVolume` to control playback levels. Both values use a **0-10** range, where `0` mutes playback. Volume changes can be deferred if the system is interrupted (for example, by a phone call).

### Choose audio output[​](#choose-audio-output "Direct link to Choose audio output")

Select the audio route with `SoundPlayingService.audioOutput`:

* **`automatic`** - uses Bluetooth A2DP when available, otherwise speaker
* **`speaker`** - forces speaker output
* **`bluetoothAsPhoneCall`** - routes audio as a phone call ⚠️

On Android, Bluetooth phone-call routing may require additional permissions and runtime requests. Ensure your permissions setup covers Bluetooth and audio settings.

#### Required permissions for Bluetooth phone-call routing[​](#required-permissions-for-bluetooth-phone-call-routing "Direct link to Required permissions for Bluetooth phone-call routing")

Add the following permissions to your `AndroidManifest.xml`:

```xml
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />

```

Also request the relevant permissions at runtime via a package like [permission\_handler](https://pub.dev/packages/permission_handler).

### Tune timing and preferences[​](#tune-timing-and-preferences "Direct link to Tune timing and preferences")

Use these settings to refine how sounds are played:

#### Sound-playing preferences (per alert severity)[​](#sound-playing-preferences-per-alert-severity "Direct link to Sound-playing preferences (per alert severity)")

Use `SoundPlayingPreferences` to control playback behavior per severity (volume, max play time, audio stream, and delay). Apply preferences with `setSoundPlayingPreferences` and read them back with `getSoundPlayingPreferences`. Use `AlertSeverity.information` and `AlertSeverity.warning` to target different alert classes.

#### Sound session request preferences (audio focus)[​](#sound-session-request-preferences-audio-focus "Direct link to Sound session request preferences (audio focus)")

Use `SoundSessionRequestPreferences` to control audio focus request behavior (category, output, delay, exclusivity). Set and get the value with the `SoundPlayingService.soundSessionRequestPreferences` property.

### Check default TTS language[​](#check-default-tts-language "Direct link to Check default TTS language")

Read `SoundPlayingService.ttsDefaultLanguage` to confirm the active system **TTS language**. If spoken output sounds mismatched, align it with the voice language configured in **Step 3**.

## Relevant examples demonstrating voice features[​](#relevant-examples-demonstrating-voice-features "Direct link to Relevant examples demonstrating voice features")

* [Human Voices](/docs/flutter/examples/routing-navigation/human-voices.md)
* [Voice Download](/docs/flutter/examples/routing-navigation/voice-download.md)
