> For the complete documentation index, see [llms.txt](https://docs.mapbox.com/help/llms.txt)

# Getting Started with the Maps SDK for Flutter

The [Mapbox Maps SDK for Flutter](https://docs.mapbox.com/flutter/) allows you to integrate interactive, customizable maps into your Flutter applications. This tutorial will guide you through creating a complete Flutter app that showcases core mapping features.

You will learn how to:

-   Add the Mapbox Maps SDK for Flutter to your project
-   Display a map with a custom camera position
-   Configure the Standard style with custom lighting
-   Add interactive map features that respond to user taps
-   Display your own data on the map with custom styling
-   Enable user location with a pulsing indicator

By the end of this tutorial, you'll have built a Flutter app featuring a map of Rio de Janeiro with an interactive marathon route, landmark interactions, and location services.

![](https://docs.mapbox.com/help/assets/ideal-img/tutorials--getting-started-flutter--final.6911bec.480.png)

## Getting started

Before you begin, you'll need:

-   **Mapbox account**: [Sign up](https://account.mapbox.com/auth/signup/) for free or [log in](https://console.mapbox.com/) if you already have an account.
-   **Flutter SDK installed.** Follow the [Flutter installation guide](https://docs.flutter.dev/get-started/install) for your operating system.
-   **CocoaPods installed.** Required for iOS development. Install using `gem install cocoapods` or follow the [CocoaPods installation guide](https://guides.cocoapods.org/using/getting-started.html).
-   **A Code editor**: A program like [Visual Studio Code](https://code.visualstudio.com/).
-   **Familiarity with Flutter development.** Beginner experience with [Flutter and Dart](https://docs.flutter.dev/get-started/codelab).

## Add the Mapbox Maps SDK for Flutter to your app

### Create a new Flutter project

Start by creating a new Flutter application. Open your terminal and run:

```bash
flutter create flutter_getting_started
cd flutter_getting_started
```

This creates a new Flutter project with the default counter app template.

### Add the Mapbox Maps SDK dependency

Open `pubspec.yaml` and add the Mapbox Maps SDK for Flutter dependency. Replace the existing dependencies section with:

```yaml
dependencies:
  flutter:
    sdk: flutter
  mapbox_maps_flutter: ^2.0.0
```

After adding the dependency, run the following command to download the package:

```bash
flutter pub get
```

### Configure your access token and create a basic map

The Mapbox Maps SDK for Flutter requires an access token to function. For security and flexibility, configure your token as an environment variable.

First, add the essential imports and configure the access token in your `main()` function:

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

void main() {
  runApp(MyApp());
  
  // Configure Mapbox access token from environment variable
  String accessToken = const String.fromEnvironment("ACCESS_TOKEN");
  MapboxOptions.setAccessToken(accessToken);
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Mapbox Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MapScreen(),
    );
  }
}

class MapScreen extends StatefulWidget {
  @override
  _MapScreenState createState() => _MapScreenState();
}

class _MapScreenState extends State<MapScreen> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: MapWidget(
        cameraOptions: CameraOptions(
          center: Point(coordinates: Position(-43.18326, -22.90796)),
          zoom: 14.5,
          bearing: -161.81,
          pitch: 70,
        ),
      ),
    );
  }
}
```

Next, create a basic app structure with a map widget:

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

void main() {
  runApp(MyApp());
  
  // Configure Mapbox access token from environment variable
  String accessToken = const String.fromEnvironment("ACCESS_TOKEN");
  MapboxOptions.setAccessToken(accessToken);
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Mapbox Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MapScreen(),
    );
  }
}

class MapScreen extends StatefulWidget {
  @override
  _MapScreenState createState() => _MapScreenState();
}

class _MapScreenState extends State<MapScreen> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: MapWidget(
        cameraOptions: CameraOptions(
          center: Point(coordinates: Position(-43.18326, -22.90796)),
          zoom: 14.5,
          bearing: -161.81,
          pitch: 70,
        ),
      ),
    );
  }
}
```

### Understanding camera positioning

The [`CameraOptions`](https://pub.dev/documentation/mapbox_maps_flutter/latest/mapbox_maps_flutter/CameraOptions-class.html) in your map widget define the initial view with specific parameters to showcase Rio de Janeiro's geography:

-   **`center`**: Coordinates pointing to Copacabana Beach area (-43.18326, -22.90796)
-   **`zoom`**: 14.5 provides a detailed neighborhood view
-   **`bearing`**: -161.81 degrees rotates the map for an optimal viewing angle
-   **`pitch`**: 70 degrees tilts the camera for a dramatic 3D perspective

This combination creates a view that highlights both the urban landscape and natural features like the coastline and Sugarloaf Mountain.

```dart
cameraOptions: CameraOptions(
  center: Point(coordinates: Position(-43.18326, -22.90796)),
  zoom: 14.5,
  bearing: -161.81,
  pitch: 70,
)
```

### Running your app

To run your app with the access token, replace `YOUR_MAPBOX_ACCESS_TOKEN` with YOUR_MAPBOX_ACCESS_TOKEN from your Mapbox account and use the `--dart-define` flag:

```bash
flutter run --dart-define=ACCESS_TOKEN=YOUR_MAPBOX_ACCESS_TOKEN
```

For more information on working with Mapbox access tokens in a Flutter project read [our guide](https://docs.mapbox.com/flutter/maps/guides/install/).

> **Note**
> 
> **iOS Platform Version**: The Mapbox Maps SDK for Flutter requires iOS 14.0 or higher. If you meet an error about minimum platform version, update your iOS deployment target:
> 
> 1.  Open `ios/Runner.xcworkspace` in Xcode
> 2.  Select the Runner project in the navigator
> 3.  Under "Deployment information", change "iOS Deployment Target" to 14.0 or higher
> 4.  Update `ios/Podfile` by changing `platform :ios, '12.0'` to `platform :ios, '14.0'`

If everything is configured correctly, you should see a map displaying Rio de Janeiro, Brazil with a tilted camera angle showing the city's dramatic coastline and mountains.

![](https://docs.mapbox.com/help/assets/ideal-img/tutorials--getting-started-flutter--basic-map.3ddf809.480.png)

## Configure the Standard style

The [Mapbox Standard style](https://docs.mapbox.com/map-styles/standard/) is a general-purpose style with configurable styling options. In this section, you'll enhance your basic map by adding event handling, style customization, and interactive landmark icons.

To configure Mapbox Standard, use the `StyleImport` as a child of `MapView`:

```jsx
<Mapview
 ...
>
  // highlight-start
  <StyleImport
    id="basemap"
    existing
    config={{
      lightPreset: 'dawn',
      showLandmarkIcons: 'true'
    }}
  />
  // highlight-end
<Mapview>
```

### Understanding style imports

The Standard style uses a style import system that allows you to configure various aspects of the map's appearance:

-   **`lightPreset`**: Controls the lighting and time-of-day appearance. Options include `day`, `dawn`, `dusk`, and `night`.
-   **`showLandmarkIcons`**: When enabled, displays interactive icons for notable landmarks.

Read the Standard Style API reference to understand the available [configuration properties](https://docs.mapbox.com/map-styles/standard/api/#configuration-properties).

When you run the app now, you'll notice the map has a warm, golden lighting that simulates dawn lighting conditions, and landmark icons appear on notable buildings and locations around Rio de Janeiro.

![](https://docs.mapbox.com/help/assets/ideal-img/tutorials--getting-started-flutter--styled-map.87fd811.480.png)

## Add a map interaction

Interactive maps respond to user input to provide information and navigation. In this section, you'll add tap interactions to the landmark icons, making them respond with animations and user feedback.

### Implement landmark tap interactions

Update your `_onMapCreated` method to include interaction handling. Add the highlighted code:

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

void main() {
  runApp(MyApp());
  
  String accessToken = const String.fromEnvironment("ACCESS_TOKEN");
  MapboxOptions.setAccessToken(accessToken);
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Mapbox Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MapScreen(),
    );
  }
}

class MapScreen extends StatefulWidget {
  @override
  _MapScreenState createState() => _MapScreenState();
}

class _MapScreenState extends State<MapScreen> {
  MapboxMap? mapboxMap;

  _onMapCreated(MapboxMap mapboxMap) async {
    this.mapboxMap = mapboxMap;
    
    // Wait for style to load, then configure it
    await mapboxMap.style.isStyleLoaded();
    
    await mapboxMap.style.setStyleImportConfigProperty(
      "basemap", 
      "lightPreset", 
      "dawn"
    );
    
    await mapboxMap.style.setStyleImportConfigProperty(
      "basemap", 
      "showLandmarkIcons", 
      true
    );

    // Add landmark icon tap interaction
    var landmarkIconTapInteraction = TapInteraction(
      FeaturesetDescriptor(
        featuresetId: "landmark-icons", 
        importId: "basemap"
      ), 
      (landmarkIcon, tapContext) {
        // Get the tapped location and landmark name
        var landmarkLocation = tapContext.point;
        var landmarkName = landmarkIcon.properties['name_en'] ?? 'unknown landmark';

        // Animate the camera to the landmark
        mapboxMap.flyTo(
          CameraOptions(center: landmarkLocation), 
          MapAnimationOptions(duration: 2000)
        );

        // Show user feedback
        ScaffoldMessenger.of(context).showSnackBar(
          SnackBar(
            content: Text("Flying to $landmarkName"),
            duration: Duration(seconds: 2),
          )
        );
      }
    );

    // Add the interaction to the map
    mapboxMap.addInteraction(landmarkIconTapInteraction);
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: MapWidget(
        cameraOptions: CameraOptions(
          center: Point(coordinates: Position(-43.18326, -22.90796)),
          zoom: 14.5,
          bearing: -161.81,
          pitch: 70,
        ),
        onMapCreated: _onMapCreated,
      ),
    );
  }
}
```

### Understanding map interactions

This interaction system works through several key components:

**[`TapInteraction`](https://pub.dev/documentation/mapbox_maps_flutter/latest/mapbox_maps_flutter/TapInteraction-class.html)**: Detects tap gestures on specific map features. You can create interactions for various gesture types including tap and long press.

**`FeaturesetDescriptor`**: Specifies which features the interaction should target. In this case:

-   `featuresetId: "landmark-icons"` targets the landmark icon features
-   `importId: "basemap"` specifies these features come from the Standard style import

Once the user has tapped a landmark icon, the `onInteraction` callback is triggered. This callback receives the tapped feature, from which you can extract properties. Additionally, the `tapContext` provides information about the tap event, including the geographic coordinates.

**Feature Properties**: Each landmark feature contains properties like:

-   `name`: Local language name
-   `name_en`: English name of the landmark

Learn more about working with map interactions in the [Interactions API documentation](https://docs.mapbox.com/flutter/maps/guides/user-interaction/interactions/).

### Test the interactions

Run your app and tap on any landmark icons visible on the map. You should see:

1.  A smooth camera animation flying to the landmark location
2.  A snackbar notification showing the landmark's name
3.  The map recentering on the selected landmark

These actions are handled by the following components:

-   **Camera Animation**: The [`flyTo`](https://pub.dev/documentation/mapbox_maps_flutter/latest/mapbox_maps_flutter/MapboxMap/flyTo.html) method smoothly animates the camera to a new position over a specified duration (2000 milliseconds = 2 seconds).
-   **Snackbar**: The `ScaffoldMessenger` displays a temporary message at the bottom of the screen.

Popular landmarks in the Rio de Janeiro area including Christ the Redeemer (Cristo Redentor), Imperial Palace, and the Museum of Tomorrow.

Your browser doesn't support HTML5 video. Open [link to the video](https://docs.mapbox.com/help/help/assets/medias/tutorials--getting-started-flutter--interactions-0a6233927832c08854abe599c79d5c64.mp4).

## Add and display your own data

Maps become more valuable when you can display your own data. In this section, you'll add a GeoJSON file containing the Rio de Janeiro Marathon route and display it as a styled line on the map.

### Download and add the GeoJSON asset

First, create an `assets` folder in your project root (at the same level as `lib/`), then download the complete Rio de Janeiro Marathon route data.

Download the marathon route GeoJSON file and add it to your project as `assets/rio_marathon.geojson`:

[Download GeoJSON](https://docs.mapbox.com/help/help/data/rio_marathon.geojson)

This file contains the complete 26.2 mile/42.195 km marathon route through Rio de Janeiro. The route showcases the city's iconic coastline, passing through neighborhoods like Copacabana, Ipanema, and along the scenic Lagoa Rodrigo de Freitas.

Next, update your `pubspec.yaml` file to include the assets:

```yaml
flutter:
  uses-material-design: true
  
  # Add the assets section
  assets:
    - assets/rio_marathon.geojson
```

Run `flutter pub get` to update your project configuration.

### Load and display the data

Update your `_onMapCreated` method to load and display the marathon route. First, add the highlighted import at the top of the file, then add the highlighted code to load and display the data:

```dart
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:mapbox_maps_flutter/mapbox_maps_flutter.dart';

void main() {
  runApp(MyApp());
  
  String accessToken = const String.fromEnvironment("ACCESS_TOKEN");
  MapboxOptions.setAccessToken(accessToken);
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Mapbox Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MapScreen(),
    );
  }
}

class MapScreen extends StatefulWidget {
  @override
  _MapScreenState createState() => _MapScreenState();
}

class _MapScreenState extends State<MapScreen> {
  MapboxMap? mapboxMap;

  _onMapCreated(MapboxMap mapboxMap) async {
    this.mapboxMap = mapboxMap;
    
    // Wait for style to load, then configure it
    await mapboxMap.style.isStyleLoaded();
    
    await mapboxMap.style.setStyleImportConfigProperty(
      "basemap", 
      "lightPreset", 
      "dawn"
    );
    
    await mapboxMap.style.setStyleImportConfigProperty(
      "basemap", 
      "showLandmarkIcons", 
      true
    );

    var landmarkIconTapInteraction = TapInteraction(
      FeaturesetDescriptor(
        featuresetId: "landmark-icons", 
        importId: "basemap"
      ), 
      (landmarkIcon, tapContext) {
        var landmarkLocation = tapContext.point;
        var landmarkName = landmarkIcon.properties['name_en'] ?? 'unknown landmark';

        mapboxMap.flyTo(
          CameraOptions(center: landmarkLocation), 
          MapAnimationOptions(duration: 2000)
        );

        ScaffoldMessenger.of(context).showSnackBar(
          SnackBar(
            content: Text("Flying to $landmarkName"),
            duration: Duration(seconds: 2),
          )
        );
      }
    );

    mapboxMap.addInteraction(landmarkIconTapInteraction);

    // Load and display the marathon route
    var marathonData = await rootBundle.loadString('assets/rio_marathon.geojson');
    
    // Add the data as a source
    await mapboxMap.style.addSource(GeoJsonSource(
      id: "rio_marathon_source", 
      data: marathonData
    ));
    
    // Add a line layer to visualize the route
    await mapboxMap.style.addLayer(LineLayer(
      id: "rio_marathon", 
      sourceId: "rio_marathon_source", 
      slot: "middle",
      lineColor: Colors.red.toARGB32(), 
      lineWidth: 6.0,
    ));
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: MapWidget(
        cameraOptions: CameraOptions(
          center: Point(coordinates: Position(-43.18326, -22.90796)),
          zoom: 14.5,
          bearing: -161.81,
          pitch: 70,
        ),
        onMapCreated: _onMapCreated,
      ),
    );
  }
}
```

### Understanding data sources and layers

This data visualization system uses two key concepts:

**Data Sources**: Sources provide the raw data that layers use for rendering. The [`GeoJsonSource`](https://pub.dev/documentation/mapbox_maps_flutter/latest/mapbox_maps_flutter/GeoJsonSource-class.html) accepts GeoJSON data either as a string or from a URL. Sources don't directly affect the map's appearance—they only provide data.

**Layers**: Layers define how data from sources should be rendered. The [`LineLayer`](https://pub.dev/documentation/mapbox_maps_flutter/latest/mapbox_maps_flutter/LineLayer-class.html) renders line geometries with configurable styling:

-   `id`: Unique identifier for the layer
-   `sourceId`: References the data source to use
-   `slot`: Controls layer ordering ("middle" places it between labels and background)
-   `lineColor`: Color of the line (converted to ARGB format)
-   `lineWidth`: Width of the line in pixels

**Layer Slots**: The Standard style provides predefined slots that help with layer ordering:

-   `bottom`: Below all other layers
-   `middle`: Between background and labels
-   `top`: Above all other layers

When you run the app now, you'll see a red line tracing the marathon route through Rio de Janeiro's streets, overlaid on the styled map with interactive landmarks.

![](https://docs.mapbox.com/help/assets/ideal-img/tutorials--getting-started-flutter--with-data.eea10a5.480.png)

## Add user location

Location services help users understand their position relative to map content. The Mapbox Maps SDK for Flutter provides a built-in location component with customizable appearance and behavior.

### Enable the location component

Add location functionality by updating your `_onMapCreated` method with the highlighted code:

```dart
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:mapbox_maps_flutter/mapbox_maps_flutter.dart';

void main() {
  runApp(MyApp());
  
  String accessToken = const String.fromEnvironment("ACCESS_TOKEN");
  MapboxOptions.setAccessToken(accessToken);
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Mapbox Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MapScreen(),
    );
  }
}

class MapScreen extends StatefulWidget {
  @override
  _MapScreenState createState() => _MapScreenState();
}

class _MapScreenState extends State<MapScreen> {
  MapboxMap? mapboxMap;

  _onMapCreated(MapboxMap mapboxMap) async {
    this.mapboxMap = mapboxMap;
    
    // Wait for style to load, then configure it
    await mapboxMap.style.isStyleLoaded();
    
    await mapboxMap.style.setStyleImportConfigProperty(
      "basemap", 
      "lightPreset", 
      "dawn"
    );
    
    await mapboxMap.style.setStyleImportConfigProperty(
      "basemap", 
      "showLandmarkIcons", 
      true
    );

    var landmarkIconTapInteraction = TapInteraction(
      FeaturesetDescriptor(
        featuresetId: "landmark-icons", 
        importId: "basemap"
      ), 
      (landmarkIcon, tapContext) {
        var landmarkLocation = tapContext.point;
        var landmarkName = landmarkIcon.properties['name_en'] ?? 'unknown landmark';

        mapboxMap.flyTo(
          CameraOptions(center: landmarkLocation), 
          MapAnimationOptions(duration: 2000)
        );

        ScaffoldMessenger.of(context).showSnackBar(
          SnackBar(
            content: Text("Flying to $landmarkName"),
            duration: Duration(seconds: 2),
          )
        );
      }
    );

    mapboxMap.addInteraction(landmarkIconTapInteraction);

    var marathonData = await rootBundle.loadString('assets/rio_marathon.geojson');
    
    await mapboxMap.style.addSource(GeoJsonSource(
      id: "rio_marathon_source", 
      data: marathonData
    ));
    
    await mapboxMap.style.addLayer(LineLayer(
      id: "rio_marathon", 
      sourceId: "rio_marathon_source", 
      slot: "middle",
      lineColor: Colors.red.toARGB32(), 
      lineWidth: 6.0,
    ));

    // Enable user location with pulsing animation
    await mapboxMap.location.updateSettings(LocationComponentSettings(
      enabled: true, 
      pulsingEnabled: true,
    ));
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: MapWidget(
        cameraOptions: CameraOptions(
          center: Point(coordinates: Position(-43.18326, -22.90796)),
          zoom: 14.5,
          bearing: -161.81,
          pitch: 70,
        ),
        onMapCreated: _onMapCreated,
      ),
    );
  }
}
```

### Location permissions

For the location component to work, your app needs location permissions. The specific setup depends on your target platforms:

**Android**: Add location permissions to `android/app/src/main/AndroidManifest.xml`:

```xml
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
```

**iOS**: Add location usage descriptions to `ios/Runner/Info.plist`:

```xml
<key>NSLocationWhenInUseUsageDescription</key>
<string>This app needs location access to show your position on the map.</string>
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>This app needs location access to show your position on the map.</string>
```

### Location component features

The [`LocationComponentSettings`](https://pub.dev/documentation/mapbox_maps_flutter/latest/mapbox_maps_flutter/LocationComponentSettings-class.html) provides several customization options:

-   **`enabled`**: Shows/hides the location component
-   **`pulsingEnabled`**: Adds a pulsing animation around the location dot
-   **`puckBearingEnabled`**: Shows device orientation as an arrow
-   **`showAccuracyRing`**: Displays location accuracy as a circle

When location permission is granted and GPS is available, you'll see a blue dot with a pulsing animation indicating the user's current location on the map.

> **Note**
> 
> **Testing Location**: When testing in a simulator, you can simulate location by setting a custom location in your development environment. On Android Studio's emulator, use the location controls in the extended controls panel. On iOS Simulator, use the "Features > Location" menu to set a custom location.

![](https://docs.mapbox.com/help/assets/ideal-img/tutorials--getting-started-flutter--location.0378501.480.png)

## Next steps

**Congratulations!** You've successfully created a Flutter app with a fully interactive Mapbox map. Your app now includes styled maps, user interactions, custom data visualization, and location services.

### Key concepts covered

-   Adding the Mapbox Maps SDK for Flutter to your project
-   Configuring access tokens securely with environment variables
-   Customizing the Standard style with lighting and landmark settings
-   Implementing tap interactions with camera animations and user feedback
-   Loading and displaying custom GeoJSON data with styled line layers
-   Enabling user location services with visual indicators

### Expand on your work

Now that you have the fundamentals, here are some ideas to enhance your mapping application:

-   **Add custom markers**: Use `PointAnnotationManager` to add custom markers at specific locations
-   **Add more data layers**: Display additional GeoJSON data like points of interest, boundaries, or real-time data
-   **Customize the location component**: Create custom location puck designs and behaviors
-   **Implement offline maps**: Download map regions for offline use with the Mapbox Maps SDK

### Learn more

> **Related content (guide): [Maps SDK for Flutter documentation](https://docs.mapbox.com/flutter/maps/guides/)**
> 
> Explore advanced features, API references, and additional customization options for Flutter maps.

> **Related content (example): [Flutter examples](https://docs.mapbox.com/flutter/maps/examples/)**
> 
> Browse code examples showing specific features and implementation patterns.