# Human voices

Last updated:

<!-- -->

October 18, 2026

<!-- -->

\| 4 minutes read

<br />

<br />

This example demonstrates the functionalities of the Maps SDK for Flutter, including route calculation, simulation of navigation, and voice instructions.

## How it works[​](#how-it-works "Direct link to How it works")

This example integrates several components to simulate navigation with voice instructions. Here’s a breakdown of the key functionalities:

* Calculate routes based on user-defined landmarks and route preferences.
* Simulate navigation along a calculated route with real-time text-to-speech (TTS) instructions.
* Display and center routes on the map, with the camera following the simulated position.

![Initial map screen](/docs/flutter/assets/images/example_flutter_human_voices1-4c563bab6be2a7344215c43cb3c92ffd.png)

**Initial map screen**

![Computed route](/docs/flutter/assets/images/example_flutter_human_voices2-c311580dbf2d443b8ce07d66c882c68b.png)

**Computed route**

![Started navigation simulation on route](/docs/flutter/assets/images/example_flutter_human_voices3-2057797cd7f3ed185f2730fa6314cedb.png)

**Started navigation simulation on route**

### Apply Human Voice[​](#apply-human-voice "Direct link to Apply Human Voice")

Before the navigation has started, select an available human voice to play the instructions.

main.dart[View on Github](https://github.com/magiclane/magiclane-maps-sdk-examples-for-flutter/tree/main/human_voices/lib/main.dart?ref_type=heads#L139)

```dart
    final voices = ContentStore.getLocalContentList(ContentType.humanVoice);

    // Apply the first voice
    SdkSettings.setVoiceByPath(voices.first.fileName);

```

### Route Calculation[​](#route-calculation "Direct link to Route Calculation")

The user can trigger route calculation, which involves defining landmarks and preferences, then using RoutingService.calculateRoute to compute the route.

main.dart[View on Github](https://github.com/magiclane/magiclane-maps-sdk-examples-for-flutter/tree/main/human_voices/lib/main.dart?ref_type=heads#L145)

```dart
  void _onBuildRouteButtonPressed(BuildContext context) {
    _showSnackBar(context, message: 'The route is calculating.');

    // Define landmarks.
    final departureLandmark = Landmark.withLatLng(
      latitude: 48.87586,
      longitude: 2.30311,
    );
    final intermediaryPointLandmark = Landmark.withLatLng(
      latitude: 48.87422,
      longitude: 2.29952,
    );
    final destinationLandmark = Landmark.withLatLng(
      latitude: 48.87361,
      longitude: 2.29513,
    );

    // Define the route preferences.
    final routePreferences = RoutePreferences();

    // Calling the calculateRoute SDK method.
    // (err, results) - is a callback function that gets called when the route computing is finished.
    // err is an error enum, results is a list of routes.
    _routingHandler = RoutingService.calculateRoute(
      [departureLandmark, intermediaryPointLandmark, destinationLandmark],
      routePreferences,
      (err, routes) {
        // If the route calculation is finished, we don't have a progress listener anymore.
        _routingHandler = null;
        ScaffoldMessenger.of(context).clearSnackBars();

        // If there aren't any errors, we display the routes.
        if (err == GemError.success) {
          // Get the routes collection from map preferences.
          final routesMap = _mapController.preferences.routes;

          // Display the routes on map.
          for (final route in routes) {
            routesMap.add(
              route,
              route == routes.first,
              label: getMapLabel(route),
            );
          }

          // Center the camera on routes.
          _mapController.centerOnRoutes(routes: routes);

          setState(() => _areRoutesBuilt = true);
        }
      },
    );
  }

```

### Simulated Navigation[​](#simulated-navigation "Direct link to Simulated Navigation")

Once the route is built, the user can start the navigation simulation. The simulation triggers instructions play using the included human voices.

main.dart[View on Github](https://github.com/magiclane/magiclane-maps-sdk-examples-for-flutter/tree/main/human_voices/lib/main.dart?ref_type=heads#L199)

```dart
  void _startSimulation() {
    final routes = _mapController.preferences.routes;

    if (routes.mainRoute == null) {
      _showSnackBar(context, message: "No main route available");
      return;
    }

    _navigationHandler = NavigationService.startSimulation(
      routes.mainRoute!,
      onNavigationInstruction: (instruction, events) {
        setState(() {
          _isSimulationActive = true;
        });
        currentInstruction = instruction;
      },
      onError: (error) {
        // If the navigation has ended or if and error occurred while navigating, remove routes.
        setState(() {
          _isSimulationActive = false;
          _cancelRoute();
        });

        if (error != GemError.cancel) {
          _stopSimulation();
        }
        return;
      },
      speedMultiplier: 20,
    );

    // Set auto play sound to true, so that the voice instructions will be played automatically
    SoundPlayingService.canPlaySounds = true;

    // Set the camera to follow position.
    _mapController.startFollowingPosition();
  }

```

The canPlaySounds flag controlls if the SDK should automatically play TTS instructions using the selected voice.

### Top Navigation Instruction Panel[​](#top-navigation-instruction-panel "Direct link to Top Navigation Instruction Panel")

top\_navigation\_panel.dart[View on Github](https://github.com/magiclane/magiclane-maps-sdk-examples-for-flutter/tree/main/human_voices/lib/top_navigation_panel.dart?ref_type=heads#L13)

```dart
class TopNavigationPanel extends StatelessWidget {
  final NavigationInstruction instruction;

  const TopNavigationPanel({super.key, required this.instruction});

  @override
  Widget build(BuildContext context) {
    return Container(
      width: MediaQuery.of(context).size.width - 20,
      height: MediaQuery.of(context).size.height * 0.2,
      padding: const EdgeInsets.all(10),
      decoration: BoxDecoration(
        color: Colors.black,
        borderRadius: BorderRadius.circular(15),
      ),
      child: Row(
        mainAxisAlignment: MainAxisAlignment.start,
        children: [
          Container(
            padding: const EdgeInsets.all(20),
            width: 100,
            child:
                instruction.nextTurnDetails != null &&
                    instruction.nextTurnDetails!.abstractGeometryImg.isValid
                ? Image.memory(
                    instruction.nextTurnDetails!.abstractGeometryImg
                        .getRenderableImageBytes(
                          size: Size(200, 200),
                          format: ImageFileFormat.png,
                        )!,
                    gaplessPlayback: true,
                  )
                : const SizedBox(), // Empty widget
          ),
          SizedBox(
            width: MediaQuery.of(context).size.width - 150,
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              mainAxisAlignment: MainAxisAlignment.start,
              children: [
                Text(
                  getFormattedDistanceToNextTurn(instruction),
                  textAlign: TextAlign.left,
                  style: const TextStyle(
                    color: Colors.white,
                    fontSize: 25,
                    fontWeight: FontWeight.w600,
                  ),
                  overflow: TextOverflow.ellipsis,
                ),
                Text(
                  instruction.nextStreetName,
                  style: const TextStyle(
                    color: Colors.white,
                    fontSize: 20,
                    fontWeight: FontWeight.w600,
                  ),
                  overflow: TextOverflow.ellipsis,
                ),
              ],
            ),
          ),
        ],
      ),
    );
  }
}

```
