Skip to content

Add a Popup in Flutter

This tutorial shows how to display popups (info windows) on markers in your MapMetrics Flutter map. Popups are great for showing extra information when a user taps on a marker.

Prerequisites

Before you begin, ensure you have:

Basic Popup with InfoWindow

The simplest way to show a popup is by using the InfoWindow property on a Marker:

dart
import 'package:flutter/material.dart';
import 'package:mapmetrics/mapmetrics.dart';

class PopupExampleScreen extends StatefulWidget {
  @override
  _PopupExampleScreenState createState() => _PopupExampleScreenState();
}

class _PopupExampleScreenState extends State<PopupExampleScreen> {
  MapMetricsController? mapController;

  final Set<Marker> markers = {
    Marker(
      markerId: MarkerId('paris'),
      position: LatLng(48.852966, 2.349902),
      infoWindow: InfoWindow(
        title: 'Hello MapMetrics',
        snippet: 'A good coffee shop',
      ),
      icon: BitmapDescriptor.defaultMarkerWithHue(BitmapDescriptor.hueRed),
    ),
  };

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Popup Example')),
      body: MapMetrics(
        styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY',
        onMapCreated: (MapMetricsController controller) {
          mapController = controller;
        },
        initialCameraPosition: CameraPosition(
          target: LatLng(48.852966, 2.349902),
          zoom: 13.0,
        ),
        markers: markers,
      ),
    );
  }
}

Tap on the marker to see the popup with the title and snippet text.

Multiple Markers with Popups

Add several markers, each with their own popup content:

dart
import 'package:flutter/material.dart';
import 'package:mapmetrics/mapmetrics.dart';

class MultiplePopupsScreen extends StatefulWidget {
  @override
  _MultiplePopupsScreenState createState() => _MultiplePopupsScreenState();
}

class _MultiplePopupsScreenState extends State<MultiplePopupsScreen> {
  MapMetricsController? mapController;

  final Set<Marker> markers = {
    Marker(
      markerId: MarkerId('eiffel_tower'),
      position: LatLng(48.8584, 2.2945),
      infoWindow: InfoWindow(
        title: 'Eiffel Tower',
        snippet: 'Iconic iron lattice tower on the Champ de Mars',
      ),
      icon: BitmapDescriptor.defaultMarkerWithHue(BitmapDescriptor.hueBlue),
    ),
    Marker(
      markerId: MarkerId('louvre'),
      position: LatLng(48.8606, 2.3376),
      infoWindow: InfoWindow(
        title: 'Louvre Museum',
        snippet: 'World\'s largest art museum and historic monument',
      ),
      icon: BitmapDescriptor.defaultMarkerWithHue(BitmapDescriptor.hueGreen),
    ),
    Marker(
      markerId: MarkerId('notre_dame'),
      position: LatLng(48.8530, 2.3499),
      infoWindow: InfoWindow(
        title: 'Notre-Dame Cathedral',
        snippet: 'Medieval Catholic cathedral on the Île de la Cité',
      ),
      icon: BitmapDescriptor.defaultMarkerWithHue(BitmapDescriptor.hueOrange),
    ),
  };

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Paris Landmarks')),
      body: MapMetrics(
        styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY',
        onMapCreated: (MapMetricsController controller) {
          mapController = controller;
        },
        initialCameraPosition: CameraPosition(
          target: LatLng(48.8566, 2.3222),
          zoom: 13.0,
        ),
        markers: markers,
      ),
    );
  }
}

Custom Popup with Bottom Sheet

For richer popup content beyond a simple title and snippet, you can show a bottom sheet when a marker is tapped:

dart
import 'package:flutter/material.dart';
import 'package:mapmetrics/mapmetrics.dart';

class CustomPopupScreen extends StatefulWidget {
  @override
  _CustomPopupScreenState createState() => _CustomPopupScreenState();
}

class _CustomPopupScreenState extends State<CustomPopupScreen> {
  MapMetricsController? mapController;

  final Map<String, Map<String, String>> markerData = {
    'cafe': {
      'title': 'Le Petit Café',
      'description': 'A cozy Parisian café with excellent croissants and espresso.',
      'hours': 'Mon-Sat: 7:00 AM - 9:00 PM',
      'rating': '4.5',
    },
    'bookstore': {
      'title': 'Shakespeare & Company',
      'description': 'Historic English-language bookstore on the Left Bank.',
      'hours': 'Daily: 10:00 AM - 10:00 PM',
      'rating': '4.8',
    },
  };

  Set<Marker> get markers => {
    Marker(
      markerId: MarkerId('cafe'),
      position: LatLng(48.8530, 2.3470),
      icon: BitmapDescriptor.defaultMarkerWithHue(BitmapDescriptor.hueOrange),
    ),
    Marker(
      markerId: MarkerId('bookstore'),
      position: LatLng(48.8526, 2.3471),
      icon: BitmapDescriptor.defaultMarkerWithHue(BitmapDescriptor.hueViolet),
    ),
  };

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Custom Popups')),
      body: MapMetrics(
        styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY',
        onMapCreated: (MapMetricsController controller) {
          mapController = controller;
        },
        onMarkerTapped: (MarkerId markerId) {
          _showCustomPopup(markerId.value);
        },
        initialCameraPosition: CameraPosition(
          target: LatLng(48.8530, 2.3470),
          zoom: 17.0,
        ),
        markers: markers,
      ),
    );
  }

  void _showCustomPopup(String markerId) {
    final data = markerData[markerId];
    if (data == null) return;

    showModalBottomSheet(
      context: context,
      shape: RoundedRectangleBorder(
        borderRadius: BorderRadius.vertical(top: Radius.circular(16)),
      ),
      builder: (context) {
        return Padding(
          padding: EdgeInsets.all(20),
          child: Column(
            mainAxisSize: MainAxisSize.min,
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              Text(
                data['title']!,
                style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
              ),
              SizedBox(height: 8),
              Text(
                data['description']!,
                style: TextStyle(fontSize: 14, color: Colors.grey[700]),
              ),
              SizedBox(height: 12),
              Row(
                children: [
                  Icon(Icons.access_time, size: 16, color: Colors.grey),
                  SizedBox(width: 4),
                  Text(data['hours']!, style: TextStyle(fontSize: 13)),
                ],
              ),
              SizedBox(height: 8),
              Row(
                children: [
                  Icon(Icons.star, size: 16, color: Colors.amber),
                  SizedBox(width: 4),
                  Text(data['rating']!, style: TextStyle(fontSize: 13)),
                ],
              ),
              SizedBox(height: 16),
            ],
          ),
        );
      },
    );
  }
}

Add Popup on Map Tap

You can also show a popup when the user taps anywhere on the map:

dart
MapMetrics(
  styleUrl: 'https://gateway.mapmetrics.org/styles/YOUR_STYLE_ID?token=YOUR_API_KEY',
  onMapCreated: (controller) => mapController = controller,
  onMapClick: (Point point, LatLng coordinates) {
    showDialog(
      context: context,
      builder: (context) => AlertDialog(
        title: Text('Location'),
        content: Text(
          'Lat: ${coordinates.latitude.toStringAsFixed(6)}\n'
          'Lng: ${coordinates.longitude.toStringAsFixed(6)}',
        ),
        actions: [
          TextButton(
            onPressed: () => Navigator.pop(context),
            child: Text('OK'),
          ),
        ],
      ),
    );
  },
  initialCameraPosition: CameraPosition(
    target: LatLng(48.852966, 2.349902),
    zoom: 13.0,
  ),
)

Next Steps


Tip: For simple information, use InfoWindow. For richer content with images, buttons, or custom layouts, use a bottom sheet or dialog triggered by onMarkerTapped.