generic_map 0.14.0 copy "generic_map: ^0.14.0" to clipboard
generic_map: ^0.14.0 copied to clipboard

A generic maps api supporting Google Maps, Mapbox, OpenStreetMaps and MapLibre

Generic Map #

A simple wrapper around:

  • flutter_map
  • google_maps_flutter
  • mapbox_maps_flutter
  • maplibre_gl

This library acts as a wrapper around some of the most popular map providers in Flutter. It provides a unified API over multiple map providers, allowing you to switch between them with minimal changes to your code. This is specifically useful when you want to support multiple platforms and need to switch between map providers based on the platform. Additionally it tries to resolve some of the limitations and shortcomings of the official map providers.

✨ Features #

  • πŸ”„ Unified API: Consistent interface across multiple map providers
  • 🎯 Feature Parity: Achieve identical functionality across all map providers
  • 🎨 Widget Markers: Support for widget markers on google_maps (Beyond official API)
  • πŸ“ Mapbox Markers: Widget marker support for mapbox_maps (Not in official API)
  • πŸ“ Declarative Syntax: Simplified marker, polyline, and polygon management vs native APIs
  • πŸ’Ύ Enhanced Caching: Caching capabilities for flutter_map out of the box (thanks to flutter_map_tile_caching)
  • ⏳ Debouncing Support: Debouncing support for more reliable address resolution & improved performance
  • 🎬 Animation Support: Smooth animations for flutter_map components

Feature Comparison #

Feature flutter_map (OSM, MapBox) Google Maps Mapbox OpenGL MapLibre GL
Static Markers βœ… βœ… βœ… βœ…
Interactive Markers βœ… ❌ ❌ ❌
Polylines βœ… βœ… βœ… βœ…
Circles βœ… βœ… βœ… βœ…
Map Gestures βœ… βœ… βœ… βœ…
Heatmaps βœ… βœ… βœ… βœ…
Geofencing βœ… ❌ ❌ ❌
Caching βœ… ❌ ❌ ❌
Animations βœ… βœ… βœ… βœ…

Platform Support #

Some map providers do not support all platforms. Through PlatformMapProviderSettings you can specify which map provider to use on each platform.

Platform flutter_map (OSM, MapBox) Google Maps Mapbox OpenGL MapLibre GL
Android βœ… βœ… βœ… βœ…
iOS βœ… βœ… βœ… βœ…
Web βœ… ❌ ❌ βœ…
Desktop βœ… ❌ ❌ ❌

Installation #

Add this package to your pubspec.yaml as a git dependency:

dependencies:
  generic_map:
    git:
      url: https://github.com/lume-code/generic_map.git

To pin to a specific version, add a ref:

  generic_map:
    git:
      url: https://github.com/lume-code/generic_map.git
      ref: v0.11.1  # tag, branch, or commit SHA

This package uses gm_google_maps_flutter (a renamed fork of google_maps_flutter with editable polyline/polygon support on web). No dependency_overrides are needed -- the forked packages are bundled and resolved automatically via path dependencies.

Usage #

import 'package:generic_map/generic_map.dart';
import 'package:latlong2/latlong.dart';
import 'package:flutter/material.dart';

final map = GenericMap(
  initialLocation: Place(
    LatLng(37.7749, -122.4194), // San Francisco coordinates
    "San Francisco",
    "CA",
  ),
  platformMapProviderSettings: PlatformMapProviderSettings(
    android: MapProviderEnum.googleMaps,
    ios: MapProviderEnum.mapbox,
    web: MapProviderEnum.mapLibre,
  ),
  mapboxOptions: MapboxOptions(
    accessToken: 'YOUR_MAPBOX_ACCESS_TOKEN',
    styleUri: 'YOUR_MAPBOX_STYLE_URI',
  ),
  mapLibreOptions: MapLibreOptions(
    accessToken: 'YOUR_MAPLIBRE_ACCESS_TOKEN',
    styleUri: 'YOUR_MAPLIBRE_STYLE_URI',
  ),
  mode: MapViewMode.static,
  interactive: true,
  markers: [
    CustomMarker(
      id: "marker1",
      position: LatLng(37.7749, -122.4194),
      widget: Container(
        padding: EdgeInsets.all(8),
        decoration: BoxDecoration(
          color: Colors.white,
          borderRadius: BorderRadius.circular(10),
          boxShadow: [
            BoxShadow(
              color: Colors.grey.withOpacity(0.5),
              spreadRadius: 2,
              blurRadius: 5,
              offset: Offset(0, 3),
            ),
          ],
        ),
        child: Text(
          'San Francisco',
          style: TextStyle(fontWeight: FontWeight.bold),
        ),
      ),
    ),
  ],
  polylines: [
    PolyLineLayer(
      id: "route1",
      points: [
        LatLng(37.7749, -122.4194),
        LatLng(37.7952, -122.4028),
      ],
      color: Colors.blue,
      width: 3,
    ),
  ],
  circleMarkers: [
    CircleMarker(
      id: "circle1",
      color: Colors.red.withOpacity(0.5),
      borderColor: Colors.red,
      borderStrokeWidth: 2,
      radius: 30,
      point: LatLng(37.7833, -122.4667),
    ),
  ],
  padding: EdgeInsets.all(20),
  onMapMoved: (event) {
    print('Map moved to: ${event.latLng} (${event.type}, ${event.source})');
  },
  addressResolver: (LatLng location) async {
    // Implement your address resolving logic here
    return Place(location, "Some Address", "Some Title");
  },
);

Map movement events #

onMapMoved receives a MapMovedEvent describing two independent things:

  • type β€” where in the movement the event sits: start when the camera begins moving, idle once it has been still for mapMoveDebounce, and addressResolved when the geocoder answers for that resting position.
  • source β€” what moved the camera: MapMoveSource.gesture for the user (drag, fling, pinch, double-tap zoom, scroll wheel), programmatic for the app's own MapViewController.moveCamera / fitBounds, and unknown for a move that is neither β€” the map settling into its initial camera, a viewport change, or a provider reporting a camera change before it has ever come to rest.

The two are separate because "the map moved" is not "the user panned". Anything that follows a moving position moves the camera itself, so without source each follow step reads as the user taking over and cancels the following that produced it:

onMapMoved: (event) {
  if (event.type == MapMoveEventType.start && event.isUserGesture) {
    setState(() => following = false); // the user took over β€” stop following
  }
},

event.isUserGesture and event.isProgrammatic are shorthands for comparing source.

What each provider can tell apart

  • Google Maps, Mapbox SDK β€” both report that the camera moved and nothing about why, so generic_map reconstructs the cause from the camera calls it made: a move is programmatic from the MapViewController call until the camera next settles, and a gesture otherwise. Two consequences: a move made through the raw platform controller, reaching past MapViewController, is reported as a gesture; and a camera call that moves nothing β€” recentring on the position the camera already holds β€” has no settle to end it, so gestures for the next few seconds are reported as programmatic.
  • flutter_map β€” reports the cause itself, and generic_map maps it straight across, so what it does report is exact. It reports less, though: it emits movement start/end only for drags, two-finger gestures and double-tap zoom. Scroll-wheel zoom, keyboard panning and MapController moves produce no onMapMoved at all, so programmatic events do not occur on flutter_map β€” a follow feature has nothing to cancel there, but it also gets no idle for its own moves.