Perché flutter_map
Quando si parla di mappe in Flutter, la scelta più comune ricade su Google Maps. Tuttavia, google_maps_flutter richiede una chiave API, comporta costi al superamento delle quote e presenta limiti nella personalizzazione. Il pacchetto flutter_map è un'alternativa open source, gratuita e altamente flessibile, basata su un'architettura a layer ispirata a Leaflet.js.
È scritto interamente in Dart, funziona su tutte le piattaforme supportate da Flutter (mobile, web, desktop) e permette di usare qualsiasi provider di tile, inclusi i tile gratuiti di OpenStreetMap.
Installazione
Aggiungi le dipendenze al tuo pubspec.yaml:
dependencies:
flutter_map: ^7.0.2
latlong2: ^0.9.1
geolocator: ^13.0.1
Il pacchetto latlong2 fornisce la classe LatLng per gestire le coordinate, mentre geolocator ci servirà per la posizione dell'utente.
Una mappa di base
Creare una mappa richiede pochissimo codice. Il widget principale è FlutterMap, configurato tramite MapOptions e popolato con una lista di children (i vari layer).
import 'package:flutter/material.dart';
import 'package:flutter_map/flutter_map.dart';
import 'package:latlong2/latlong.dart';
class MappaBase extends StatelessWidget {
const MappaBase({super.key});
@override
Widget build(BuildContext context) {
return FlutterMap(
options: const MapOptions(
initialCenter: LatLng(41.9028, 12.4964), // Roma
initialZoom: 13,
),
children: [
TileLayer(
urlTemplate: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
userAgentPackageName: 'com.esempio.miaapp',
),
],
);
}
}
Importante: l'uso dei tile pubblici di OpenStreetMap è soggetto alla Tile Usage Policy. Per app in produzione con molto traffico è consigliato un provider dedicato (come MapTiler o Thunderforest) o un server di tile self-hosted.
Ricorda inoltre di impostare sempre userAgentPackageName con l'identificativo della tua app, come richiesto dalle policy.
Aggiungere i marker
Per mostrare punti di interesse si usa il MarkerLayer, che accetta una lista di Marker. Ogni marker definisce la posizione e un widget da disegnare.
MarkerLayer(
markers: [
Marker(
point: const LatLng(41.9028, 12.4964),
width: 40,
height: 40,
child: const Icon(
Icons.location_on,
color: Colors.red,
size: 40,
),
),
Marker(
point: const LatLng(41.8902, 12.4922), // Colosseo
width: 40,
height: 40,
child: GestureDetector(
onTap: () => debugPrint('Marker toccato!'),
child: const Icon(Icons.place, color: Colors.blue),
),
),
],
)
Ogni marker è un widget a tutti gli effetti: puoi renderlo interattivo con un GestureDetector, animarlo o mostrarci dentro un'immagine.
Controllare la mappa a livello programmatico
Per spostare la mappa, cambiare zoom o centrarla su una posizione, si usa il MapController.
class MappaControllata extends StatefulWidget {
const MappaControllata({super.key});
@override
State<MappaControllata> createState() => _MappaControllataState();
}
class _MappaControllataState extends State<MappaControllata> {
final _mapController = MapController();
void _vaiAMilano() {
_mapController.move(const LatLng(45.4642, 9.1900), 12);
}
@override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButton: FloatingActionButton(
onPressed: _vaiAMilano,
child: const Icon(Icons.navigation),
),
body: FlutterMap(
mapController: _mapController,
options: const MapOptions(
initialCenter: LatLng(41.9028, 12.4964),
initialZoom: 13,
),
children: [
TileLayer(
urlTemplate: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
userAgentPackageName: 'com.esempio.miaapp',
),
],
),
);
}
}
Mostrare la posizione dell'utente
Combinando geolocator con la mappa possiamo centrare la vista sulla posizione corrente dell'utente. Ricorda di configurare i permessi in AndroidManifest.xml e Info.plist.
Future<void> _centraSuUtente() async {
// Verifica e richiesta dei permessi
LocationPermission permission = await Geolocator.checkPermission();
if (permission == LocationPermission.denied) {
permission = await Geolocator.requestPermission();
if (permission == LocationPermission.denied) return;
}
final posizione = await Geolocator.getCurrentPosition();
_mapController.move(
LatLng(posizione.latitude, posizione.longitude),
15,
);
}
Disegnare percorsi e aree
Oltre ai marker, flutter_map supporta PolylineLayer per tracciare percorsi e PolygonLayer per evidenziare aree geografiche.
PolylineLayer(
polylines: [
Polyline(
points: const [
LatLng(41.9028, 12.4964),
LatLng(41.8902, 12.4922),
LatLng(41.8986, 12.4769),
],
strokeWidth: 4,
color: Colors.deepPurple,
),
],
)
Questo è utilissimo per app di navigazione, tracciamento di attività sportive o delimitazione di zone.
Gestire tanti marker con il clustering
Quando i marker sono centinaia, mostrarli tutti insieme rende la mappa illeggibile e lenta. Il pacchetto aggiuntivo flutter_map_marker_cluster raggruppa automaticamente i marker vicini in cluster che si espandono allo zoom.
dependencies:
flutter_map_marker_cluster: ^1.4.0
MarkerClusterLayerWidget(
options: MarkerClusterLayerOptions(
maxClusterRadius: 45,
markers: listaMarker,
builder: (context, markers) {
return CircleAvatar(
backgroundColor: Colors.blue,
child: Text(markers.length.toString()),
);
},
),
)
Best practice
- Rispetta le policy dei tile provider: per la produzione valuta un servizio a pagamento o self-hosted, evitando di sovraccaricare i server gratuiti di OSM.
- Imposta i limiti di zoom con
minZoomemaxZoomnelleMapOptionsper un'esperienza coerente. - Usa il clustering quando i marker superano qualche decina.
- Gestisci la cache dei tile con pacchetti come
flutter_map_tile_cachingper un uso offline e per ridurre il consumo di dati. - Non abusare dei rebuild: aggiorna i layer solo quando necessario per mantenere fluida l'esperienza.
Conclusioni
flutter_map è una soluzione matura e potente per integrare mappe nelle app Flutter senza vincoli di licenza o costi obbligatori. La sua architettura a layer lo rende estremamente componibile, permettendo di sovrapporre marker, percorsi, aree e overlay personalizzati. Per la maggior parte dei progetti che non richiedono funzionalità proprietarie di Google Maps, rappresenta la scelta più flessibile ed economica.
