Displaying Maps with Flutter
Tags: flutter, javascript, dart, api
Published: 2020-11-03 16:33:01

*Here's a simple map application which uses an open source flutter plugin that implements the JavaScript Leaflet library functionalities and allows users to display a raster map from multiple providers inside the flutter screen.*

In the last few years, Flutter has become one of the most popular cross-platform frameworks in the world. It allows users to create an application with one code base (using a Dart language) which runs on Android or iOS, and in the future, on the Web as well. 

Currently, TomTom doesn’t have a dedicated Maps SDK for Android for Flutter to display the TomTom map. In order to help developers, we’ve created a simple map application which uses an open source flutter plugin called ‘flutter_map’. The plugin implements the JavaScript Leaflet library functionalities and allows users to display a raster map from multiple providers inside the flutter screen. 

##Prerequisites

In order to start writing the application, a few steps need to be taken: 

1. First you will need a TomTom API Key. If you don't have an API key, visit the [How to Get a TomTom API key tutorial](https://developer.tomtom.com/how-to-get-tomtom-api-key) and create one.
2. Flutter must be installed on your system and must be added to the PATH variable. 

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/flwx5z7wapg8zpj6w3le.png)

To install Flutter, you can use instructions from the following site: https://flutter.dev/docs/get-started/install.

3. The Android Studio along with the Android SDK must be installed on your system. To install Android Studio you can follow a guide [here](https://developer.android.com/studio/install).

4. Flutter and Dart plugins must be installed in the Android Studio application, just like on the following screen:

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/ow9vi6tg8qzdrdier5ps.png)

5. Now you can run the flutter doctor command. When it doesn’t find any issues, then you are good to go! 

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/zh9rb4uvmhq2m6o6ztet.jpg)

##Create a New Flutter Project

In order to create a new Flutter application, you’ll need to create a new Flutter project and choose ‘Flutter Application’ like on the following screen:

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/cjj7zjhv30j1zmxaw0by.jpg)

Click ‘Next’ and give a proper name for your application, making sure that all paths are correct:

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/xoua617kod4qmq46rsmu.jpg)

Click ‘Next’, leave all default values and click ‘Finish’: 

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/tu2jqmuf8hyg7ziuq6xh.jpg)

At that point, you should be able to run a default example flutter application. 

##Displaying the TomTom Map

If the application is running correctly, you can start to modify a project by adding necessary dependencies into the pubspec.yaml file. Let’s add the ‘flutter_map’, http packages and run flutter pub to get: 

```javascript
dependencies:
  flutter_map: 0.10.1+1
  http: 0.12.2
```

After the new packages have been installed, let’s replace the source code in the main.dart file in order to display the TomTom map. The following code snippet is adding the ‘FlutterMap’ widget and place it in a center of the screen which is set to the TomTom office in Amsterdam.

```javascript
import "package:flutter/material.dart";
import "package:flutter_map/flutter_map.dart";
import "package:latlong/latlong.dart"; 
import "package:http/http.dart" as http;
import "dart:convert" as convert;

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: "Flutter Demo",
      home: HomeScreen(),
    );
  }
}

class HomeScreen extends StatelessWidget {
  final String apiKey = "YOUR_API_KEY";

  @override
  Widget build(BuildContext context) {
    final tomtomHQ = new LatLng(52.376372, 4.908066);
    return MaterialApp(
      title: "TomTom Map",
      home: Scaffold(
        body: Center(
            child: Stack(
              children: <Widget>[
                FlutterMap(
                  options: new MapOptions(center: tomtomHQ, zoom: 13.0),
                  layers: [
                    new TileLayerOptions(
                      urlTemplate: "https://api.tomtom.com/map/1/tile/basic/main/"
                          "{z}/{x}/{y}.png?key={apiKey}",
                      additionalOptions: {"apiKey": apiKey},
                    )
                  ],
                )
              ],
            )),
      ),
    );
  }
}
```

##Adding a Marker to the Map

In order to add a marker to the map, a developer needs to add an additional marker layer inside the FlutterMap widget, so that it looks like this: 

```javascript
FlutterMap(
  options: new MapOptions(center: tomtomHQ, zoom: 13.0),
  layers: [
    new TileLayerOptions(
      urlTemplate: "https://api.tomtom.com/map/1/tile/basic/main/"
          "{z}/{x}/{y}.png?key={apiKey}",
      additionalOptions: {"apiKey": apiKey},
    ),
    new MarkerLayerOptions(
      markers: [
        new Marker(
          width: 80.0,
          height: 80.0,
          point: tomtomHQ,
          builder: (BuildContext context) => const Icon(
              Icons.location_on,
              size: 60.0,
              color: Colors.black),
        ),
      ],
    ),
  ],
)
```

##Adding the TomTom Logo Image

According to the Terms and Conditions of TomTom Maps API, a developer also needs to add a TomTom logo to the application. Let’s download the image from https://d221h2fa9j1k6s.cloudfront.net/tomtom-guides/flutter-map-example/tt_logo.png and place it in the newly created images folder: 

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/10aaned4tkh6nxmd0h2j.png)

In order to use a newly created logo in the application, a new asset needs to be added inside the pubspec.yaml inside the ‘flutter’ section: 

```javascript
assets:

  - images/tt_logo.png
```

Now the image can be added as a new child of the Stack widget, right next to the FlutterMap widget. The image is wrapped inside a Container so that it can be positioned easily on the screen: 

```javascript
@override
Widget build(BuildContext context) {
  final tomtomHQ = new LatLng(52.376372, 4.908066);
  return MaterialApp(
    title: "TomTom Map",
    home: Scaffold(
      body: Center(
          child: Stack(
            children: <Widget>[
              FlutterMap(
                options: new MapOptions(center: tomtomHQ, zoom: 13.0),
                layers: [
                  new TileLayerOptions(
                    urlTemplate: "https://api.tomtom.com/map/1/tile/basic/main/"
                        "{z}/{x}/{y}.png?key={apiKey}",
                    additionalOptions: {"apiKey": apiKey},
                  ),
                  new MarkerLayerOptions(
                    markers: [
                      new Marker(
                        width: 80.0,
                        height: 80.0,
                        point: new LatLng(52.376372, 4.908066),
                        builder: (BuildContext context) => const Icon(
                            Icons.location_on,
                            size: 60.0,
                            color: Colors.black),
                      ),
                    ],
                  ),
                ],
              ),
              Container(
                  padding: EdgeInsets.all(20),
                  alignment: Alignment.bottomLeft,
                  child: Image.asset("images/tt_logo.png"))
            ],
          )),
    ),
  );
}
```

After saving the file, the TomTom logo should appear at the bottom left side of the screen just like on the following screen: 

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/jv0w0uvtynk494s7ie1x.jpg)

##Implementing the TomTom Copyright API

According to the Terms and Conditions, a developer needs to implement the Copyright API as well. Let’s do that by adding a simple floating action button to the application Scaffold widget in the Home section: 

```javascript
@override
Widget build(BuildContext context) {
  final tomtomHQ = new LatLng(52.376372, 4.908066);
  return MaterialApp(
    title: "TomTom Map",
    home: Scaffold(
      body: Center(
          child: Stack(
            children: <Widget>[
              FlutterMap(
                options: new MapOptions(center: tomtomHQ, zoom: 13.0),
                layers: [
                  new TileLayerOptions(
                    urlTemplate: "https://api.tomtom.com/map/1/tile/basic/main/"
                        "{z}/{x}/{y}.png?key={apiKey}",
                    additionalOptions: {"apiKey": apiKey},
                  ),
                  new MarkerLayerOptions(
                    markers: [
                      new Marker(
                        width: 80.0,
                        height: 80.0,
                        point: new LatLng(52.376372, 4.908066),
                        builder: (BuildContext context) => const Icon(
                            Icons.location_on,
                            size: 60.0,
                            color: Colors.black),
                      ),
                    ],
                  ),
                ],
              ),
              Container(
                  padding: EdgeInsets.all(20),
                  alignment: Alignment.bottomLeft,
                  child: Image.asset("images/tt_logo.png"))
            ],
          )),
      floatingActionButton: FloatingActionButton(
        child: Icon(Icons.copyright),
        onPressed: () async {
        },
      ),
    ),
  );
}
```

A new Floating Action Button should appear on the application screen, just like on the following image:

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/9wukf1ihq9wswpy5sz21.jpg)

Now let’s add a new file which will contain a simple widget displaying a scrollable text. 

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/aunfeb9ciqutb7rjuji9.png)

Put the following source code in the newly added copyrights_page.dart file: 

```javascript
import 'package:flutter/material.dart';

class CopyrightsPage extends StatelessWidget {
  final String copyrightsText;

  CopyrightsPage({Key key, @required this.copyrightsText}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("TomTom Maps API - Copyrights"),
      ),
      body: Container(
        child: Column(
          children: [
            Expanded(
                child: SingleChildScrollView(
                  child: Container(
                      padding: EdgeInsets.all(20), child: Text(copyrightsText)),
                )),
          ],
        ),
      ),
    );
  }
}
```

And import the new copyrights_page.dart file inside a main.dart: 

```javascript
import "package:flutter_app/copyrights_page.dart";
```

Now let’s use the TomTom Copyrights API by creating the getCopyrightsJSONResponse() method and call it when the Floating Action Button will be pressed.  

```javascript
 @override
  Widget build(BuildContext context) {
    final tomtomHQ = new LatLng(52.376372, 4.908066);
    return MaterialApp(
      title: "TomTom Map",
      home: Scaffold(
        body: Center(
            child: Stack(
              children: <Widget>[
                FlutterMap(
                  options: new MapOptions(center: tomtomHQ, zoom: 13.0),
                  layers: [
                    new TileLayerOptions(
                      urlTemplate: "https://api.tomtom.com/map/1/tile/basic/main/"
                          "{z}/{x}/{y}.png?key={apiKey}",
                      additionalOptions: {"apiKey": apiKey},
                    ),
                    new MarkerLayerOptions(
                      markers: [
                        new Marker(
                          width: 80.0,
                          height: 80.0,
                          point: new LatLng(52.376372, 4.908066),
                          builder: (BuildContext context) => const Icon(
                              Icons.location_on,
                              size: 60.0,
                              color: Colors.black),
                        ),
                      ],
                    ),
                  ],
                ),
                Container(
                    padding: EdgeInsets.all(20),
                    alignment: Alignment.bottomLeft,
                    child: Image.asset("images/tt_logo.png"))
              ],
            )),
        floatingActionButton: FloatingActionButton(
          child: Icon(Icons.copyright),
          onPressed: () async {
            http.Response response = await getCopyrightsJSONResponse();
          },
        ),
      ),
    );
  }

  Future<http.Response> getCopyrightsJSONResponse() async {
    var url = "https://api.tomtom.com/map/1/copyrights.json?key=$apiKey";
    var response = await http.get(url);
    return response;
  }

}
```

In order to parse the response from the API, let’s create the parseCopyrightsResponse method along with a few more helper methods like parseRegionsCopyrights and parseGeneralCopyrights. Pass the parsing results into the copyrights screen and show it by calling using the Navigator: 

```javascript
import "package:flutter/material.dart";
import "package:flutter_map/flutter_map.dart";
import "package:latlong/latlong.dart";
import "package:http/http.dart" as http;
import "dart:convert" as convert;
import "package:flutter_app/copyrights_page.dart";

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: "Flutter Demo",
      home: HomeScreen(),
    );
  }
}

class HomeScreen extends StatelessWidget {
  final String apiKey = "YOUR_API_KEY";

  @override
  Widget build(BuildContext context) {
    final tomtomHQ = new LatLng(52.376372, 4.908066);
    return MaterialApp(
      title: "TomTom Map",
      home: Scaffold(
        body: Center(
            child: Stack(
              children: <Widget>[
                FlutterMap(
                  options: new MapOptions(center: tomtomHQ, zoom: 13.0),
                  layers: [
                    new TileLayerOptions(
                      urlTemplate: "https://api.tomtom.com/map/1/tile/basic/main/"
                          "{z}/{x}/{y}.png?key={apiKey}",
                      additionalOptions: {"apiKey": apiKey},
                    ),
                    new MarkerLayerOptions(
                      markers: [
                        new Marker(
                          width: 80.0,
                          height: 80.0,
                          point: new LatLng(52.376372, 4.908066),
                          builder: (BuildContext context) => const Icon(
                              Icons.location_on,
                              size: 60.0,
                              color: Colors.black),
                        ),
                      ],
                    ),
                  ],
                ),
                Container(
                    padding: EdgeInsets.all(20),
                    alignment: Alignment.bottomLeft,
                    child: Image.asset("images/tt_logo.png"))
              ],
            )),
        floatingActionButton: FloatingActionButton(
          child: Icon(Icons.copyright),
          onPressed: () async {
            http.Response response = await getCopyrightsJSONResponse();

            Navigator.push(
                context,
                MaterialPageRoute(
                    builder: (context) => CopyrightsPage(
                        copyrightsText: parseCopyrightsResponse(response))));
          },
        ),
      ),
    );
  }

  Future<http.Response> getCopyrightsJSONResponse() async {
    var url = "https://api.tomtom.com/map/1/copyrights.json?key=$apiKey";
    var response = await http.get(url);
    return response;
  }

  String parseCopyrightsResponse(http.Response response) {
    if (response.statusCode == 200) {
      StringBuffer stringBuffer = StringBuffer();
      var jsonResponse = convert.jsonDecode(response.body);
      parseGeneralCopyrights(jsonResponse, stringBuffer);
      parseRegionsCopyrights(jsonResponse, stringBuffer);
      return stringBuffer.toString();
    }
    return "Can't get copyrights";
  }

  void parseRegionsCopyrights(jsonResponse, StringBuffer sb) {
    List<dynamic> copyrightsRegions = jsonResponse["regions"];
    copyrightsRegions.forEach((element) {
      sb.writeln(element["country"]["label"]);
      List<dynamic> cpy = element["copyrights"];
      cpy.forEach((e) {
        sb.writeln(e);
      });
      sb.writeln("");
    });
  }

  void parseGeneralCopyrights(jsonResponse, StringBuffer sb) {
    List<dynamic> generalCopyrights = jsonResponse["generalCopyrights"];
    generalCopyrights.forEach((element) {
      sb.writeln(element);
      sb.writeln("");
    });
    sb.writeln("");
  }
}
```

Now the copyrights screen should be visible: 

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/2dmqxeaw1ndjbgmg9hfi.jpg)

[The full source code of the application can be found here in our GitHub](https://github.com/szczepanczyk/TomTom-Flutter-Map).  

Happy coding! 

*This article originally appeared on https://developer.tomtom.com/blog. The original author is Mateusz Szczepańczyk.*

**For more tutorials, toolkits, demos, and more, check out the [TomTom Developer Portal](https://developer.tomtom.com). Grab your free API key and get building today.**