Skip to content

Search Along Route

In this guide, you will learn how to calculate a route, simulate navigation, and search for landmarks along the route.

Search Along Route - example Flutter screenshot

Setup

First, get an API key token, see the Getting Started guide.

Prerequisites

Make sure you completed the Environment Setup - Flutter Examples guide before starting this guide.

Build and run

Go to the search_along_route directory, within the Flutter examples directory - that is the name of this example project.

Search Along Route - example Flutter screenshot

Note - the gem_kit directory containing the Maps SDK for Flutter should be in the plugins directory of the example, e.g. example_pathname/plugins/gem_kit - see the environment setup guide above.

Run: flutter pub get

Configure the native parts:

First, verify that the ANDROID_SDK_ROOT environment variable is set to the root path of your android SDK.

In android/build.gradle add the maven block as shown, within the allprojects block, for both debug and release builds:

:lineno-start: 1

    allprojects {
        repositories {
            google()
            mavenCentral()
            maven {
               url "${rootDir}/../plugins/gem_kit/android/build"
            }
        }
    }

in android/app/build.gradle within the android block, in the defaultConfig block, the android SDK version minSdk must be set as shown below.

Additionally, for release builds, in android/app/build.gradle, within the android block, add the buildTypes block as shown:

Replace example_pathname with the actual project pathname

:lineno-start: 1

android {
    defaultConfig {
        applicationId "com.magiclane.gem_kit.examples.example_pathname"
        minSdk 21
        targetSdk flutter.targetSdk
        versionCode flutterVersionCode.toInteger()
        versionName flutterVersionName
    }
    buildTypes {
        release {
            minifyEnabled false
            shrinkResources false

            // TODO: Add your own signing config for the release build.
            // Signing with the debug keys for now, so `flutter run --release` works.
            signingConfig signingConfigs.debug
        }
    }
}

In the ios/Podfile configuration file, at the top, set the minimum ios platform version to 14 like this:

platform :ios, '14.0'

We recommend you to run these commands after you copy the gem_kit into your project: |flutter clean |flutter pub get |and |cd ios |pod install

Then run the project:

flutter run --debug
or
flutter run --release

App entry and initialization

const projectApiToken = String.fromEnvironment('GEM_TOKEN');

void main() {
  runApp(const MyApp());
}

This code initializes the projectApiToken with the required authorization token and launches the app.

How it works

Search Along Route - example Flutter screenshot

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      debugShowCheckedModeBanner: false,
      title: 'Search Along Route',
      home: MyHomePage(),
    );
  }
}
class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key});

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  late GemMapController _mapController;
  bool _isSimulationActive = false;
  bool _areRoutesBuilt = false;

  // We use the handler to cancel the route calculation.
  TaskHandler? _routingHandler;

  // We use the handler to cancel the navigation.
  TaskHandler? _navigationHandler;

  @override
  void dispose() {
    GemKit.release();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Colors.deepPurple[900],
        title: const Text("Search Along Route", style: TextStyle(color: Colors.white)),
        leading: Row(
          children: [
            if (_areRoutesBuilt)
              IconButton(
                onPressed: _searchAlongRoute,
                icon: const Icon(Icons.search, color: Colors.white),
              ),
          ],
        ),
        actions: [
          if (!_isSimulationActive && _areRoutesBuilt)
            IconButton(
              onPressed: _startSimulation,
              icon: const Icon(Icons.play_arrow, color: Colors.white),
            ),
          if (_isSimulationActive)
            IconButton(
              onPressed: _stopSimulation,
              icon: const Icon(
                Icons.stop,
                color: Colors.white,
              ),
            ),
          if (!_areRoutesBuilt)
            IconButton(
              onPressed: () => _onBuildRouteButtonPressed(),
              icon: const Icon(
                Icons.route,
                color: Colors.white,
              ),
            ),
        ],
      ),
      body: GemMap(onMapCreated: _onMapCreated, appAuthorization: projectApiToken),
    );
  }

  void _onMapCreated(GemMapController controller) {
    _mapController = controller;
  }

Route Calculation

This section shows how to calculate a route between two landmarks and display it on the map.

Future<void> _onBuildRouteButtonPressed() async {
  // Define the departure.
  final departureLandmark = Landmark.withLatLng(latitude: 37.77903, longitude: -122.41991);

  // Define the destination.
  final destinationLandmark = Landmark.withLatLng(latitude: 37.33619, longitude: -121.89058);

  // Define the route preferences.
  final routePreferences = RoutePreferences();
  _showSnackBar(context, message: 'The route is calculating.');

  _routingHandler =
      RoutingService.calculateRoute([departureLandmark, destinationLandmark], routePreferences, (err, routes) async {
    // 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: route.getMapLabel());
      }

      _mapController.centerOnRoute(routes.first);
    }

    setState(() {
      _areRoutesBuilt = true;
    });
  });
}

Search Along Route

The following code shows how to search for landmarks along the calculated route. The search results are printed to the console.

void _searchAlongRoute() {
  if (!_areRoutesBuilt) return;

  final routes = _mapController.preferences.routes;

  // Calling the search along route SDK method.
  // (err, results) - is a callback function that gets called when the search is finished.
  // err is an error enum, results is a list of landmarks.
  SearchService.searchAlongRoute(routes.mainRoute, (err, results) {
    if (err != GemError.success || results == null) {
      print("SearchAlongRoute - no results found");
      return;
    }

    print("SearchAlongRoute - ${results.length} results:");
    for (final Landmark landmark in results) {
      final landmarkName = landmark.name;
      print("SearchAlongRoute: $landmarkName");
    }
  });
}

Utility Functions

Utility functions are defined to show messages and format route labels.

void _showSnackBar(BuildContext context, {required String message, Duration duration = const Duration(hours: 1)}) {
  final snackBar = SnackBar(
    content: Text(message),
    duration: duration,
  );

  ScaffoldMessenger.of(context).showSnackBar(snackBar);
}

extension RouteExtension on Route {
  String getMapLabel() {
    final totalDistance = getTimeDistance().unrestrictedDistanceM + getTimeDistance().restrictedDistanceM;
    final totalDuration = getTimeDistance().unrestrictedTimeS + getTimeDistance().restrictedTimeS;

    return '${_convertDistance(totalDistance)} \n${_convertDuration(totalDuration)}';
  }

  String _convertDistance(int meters) {
    if (meters >= 1000) {
      double kilometers = meters / 1000;
      return '${kilometers.toStringAsFixed(1)} km';
    } else {
      return '${meters.toString()} m';
    }
  }

  String _convertDuration(int seconds) {
    int hours = seconds ~/ 3600; // Number of whole hours
    int minutes = (seconds % 3600) ~/ 60; // Remaining whole minutes

    return '${hours}h ${minutes}m';
  }
}

In this example, you learned how to calculate a route, simulate navigation, and search for landmarks along the route using the ``gem_kit``package.

Flutter Examples

Maps SDK for Flutter Examples can be downloaded or cloned with Git