Getting started with MapKit for Flutter

This tutorial explains how install and setup MapKit library and create a map with a placemark for a specific location.

Step 1. Get the MapKit API key

Before you can use MapKit SDK in your application, you need the API key.

Go to the Mappable Account and register a new account or log in to an existing. The main page will display a key that is suitable for any Mappable service.

Step 2. Add the MapKit library to your project

The MapKit SDK library is available in the pub.dev repository.

  1. Create a new project or open an existing one in tools like Visual Studio Code or Android Studio.

  2. Open the project's pubspec.yaml file. In the dependencies section, add the following dependency:

    dependencies:
      # The lite library only contains the map, traffic layer,
      # LocationManager, and UserLocationLayer
      # and lets you download offline maps (in the paid version only).
      mappable_maps_mapkit_lite:
        version: ^1.1.0
    
      # The full library supplements lite version features with car routing,
      # bike routing, pedestrian routing, and public transport routing,
      # search, suggest, geocoding, and panorama display.
      # mappable_maps_mapkit:
      #   version: ^1.1.0
    
  3. Run pub get to sync the project and apply the changes.

    If synchronization succeeds, the library is automatically added to the project when it's compiled.

  4. Add platform dependencies to ./android/app/build.gradle:

    implementation "com.google.android.gms:play-services-location:21.1.0"
    
    // Work Manager dependency. You should add it if you want to use offline maps
    // implementation "androidx.work:work-runtime:2.9.0"
    

Step 3. Provide the API key to MapKit

The MapKit SDK requires you to initialize the library and set up the API key in initMapkit function.

We recommend doing that in your main() function:

import 'package:mappable_maps_mapkit_lite/init.dart' as init;

void main() async {
  WidgetsFlutterBinding.ensureInitialized();

  await init.initMapkit(
    apiKey: 'YOUR_API_KEY'
  );
}

Warning

The init.initMapkit(String apiKey) call loads the MapKit's required native libraries.

If you don't want to put your API key under a version control system, you can:

  1. Define the compilation-time flag:

    flutter run --dart-define MAPKIT_API_KEY=your_api_key
    

    And use it this way:

    import 'package:mappable_maps_mapkit_lite/init.dart' as init;
    
    void main() async {
      WidgetsFlutterBinding.ensureInitialized();
    
      final mapkitApiKey = String.fromEnvironment('MAPKIT_API_KEY');
    
      await init.initMapkit(
        apiKey: mapkitApiKey
      );
    }
    
  2. You can also use the ENVied package or create .dart file, which would contain a global variable with the API key.

Note

Make sure your VCS ignores the files containing the API keys.

Step 4. Add the map

  1. Add MappableMap to the widgets tree:

    void main() async {
      WidgetsFlutterBinding.ensureInitialized();
    
      await init.initMapkit(
        apiKey: 'YOUR_API_KEY'
      );
    
      runApp(const MyApp());
    }
    
    class MyApp extends StatefulWidget {
      const MyApp({super.key});
    
      @override
      State<MyApp> createState() => _MyAppState();
    }
    
    class _MyAppState extends State<MyApp> {
    
      MapWindow? _mapWindow;
    
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          home: Scaffold(
            body: MappableMap(onMapCreated: (mapWindow) => _mapWindow = mapWindow)
          )
        );
      }
    }
    
  2. When the map becomes visible or invisible to the user, send onStart and onStop events to MapKit using mapkit.onStart() and mapkit.onStop methods respectively.
    Otherwise, MapKit won't be able to display the map and will stop processing it when the app becomes invisible to the user.

Build and run your application. There's an example of the Activity with the tappable map:

Map with the smallest zoom

Maps support multiple actions by default: move, rotate, change the zoom, and tilt.

Without additional setup, the map will be shown with the smallest possible zoom for the user's screen.

To change a map's position or zoom, use the Map.moveWithAnimation method:

map.move(
  CameraPosition(
    Point(latitude: 25.1982, longitude: 55.272758),
    zoom: 17.0,
    azimuth: 150.0,
    tilt: 30.0
  )
);

The Map.moveWithAnimation call accepts the CameraPosition argument, which fully defines the map's position, zoom, tilt, and azimuth.

There's an example of the Activity after applying the move to the map:

Map after applied move

Step 5. Note the following

MapKit stores weak references to the Listener objects passed to it. You need to store references to them in memory yourself:

final class MapCameraListenerImpl implements MapCameraListener {
  // ......
}

final class SomeMapScopedClass {

  final MapWindow _mapWindow;
  final MapCameraListener _cameraListener = MapCameraListenerImpl();

  SomeMapScopedClass(this._mapWindow);

  void addListener() {
    _mapWindow.map.addCameraListener(_cameraListener);
  }
}

Note

By default, the methods of any Listener objects and platform interfaces are called on the main thread unless the method documentation specifies otherwise.

Step 6. Display a placemark on the map

Let's modify the application such that you can show a tappable placemark on the map.

  1. Add a png resource for the placemark image to the project.

    For example, we have the image, and it's accessible by the assets/ic_pin.png identificator.

    Icon placemark

  2. Add the placemark for the Map.mapObjects collection to the specific location.

    Use ImageProvider.fromImageProvider to create an ImageProvider instance to get a placemark image.

    final imageProvider = ImageProvider.fromImageProvider(const AssetImage("assets/ic_pin.png"));
    final placemark = mapWindow.map.mapObjects.addPlacemark()
      ..geometry = const Point(latitude: 25.1982, longitude: 55.272758)
      ..setIcon(imageProvider);
    
  3. To subscribe to created placemark's taps use MapObject.addTapListener method.

    final class MapObjectTapListenerImpl implements MapObjectTapListener {
    
      @override
      bool onMapObjectTap(MapObject mapObject, Point point) {
        showSnackBar("Tapped the placemark: Point(latitude: ${point.latitude}, longitude: ${point.longitude})");
        return true;
      }
    }
    
    final listener = MapObjectTapListenerImpl();
    placemark.addTapListener(listener);
    

Build and run your application. There's a placemark with your custom image on the map. Tap the placemark, and the message toast will show up:

Map after placemark was tapped