Nella scorsa lezione, abbiamo visto come integrare la localizzazione dell’utente attraverso la libreria geolocator.
In questa, vedremo come utilizzare le mappe di Google per rendere l’esperienza utente ancora più immersiva.
Esempio pratico
Come sempre, creiamo un nuovo progetto come descritto nella lezione 6 e procediamo con una semplice applicazione per vedere come:
- integrare le mappe di Google nella nostra app;
- aggiungere un marker;
- mostrare la posizione corrente dell’utente.
La struttura del nostro progetto sarà quindi la seguente
lib/
│── screens/
│ │── screen1
│ │ │── screen1.dart
│── theme/
│ │── style.dart
│── routes.dart
Diversamente dalle ultime lezioni, in questo esempio ci basterà lavorare all’interno di una singola schermata per estrapolare l’informazione di interesse. La definizione dello style
e delle routes
è disponibile sul repository GitHub menzionato nella lezione precedente.
Installazione del plugin google_maps_flutter
Apriamo il file pubspec.yaml e inseriamo sotto la voce dependencies
il nome del plugin di interesse come segue
dependencies:
# . . .
google_maps_flutter: ^0.5.27+1
Eseguiamo dal nostro terminale il comando:
flutter pub get
per installare il plugin.
Definizione dei permessi e della API Key
Per permettere alla nostra applicazione di utilizzare le mappe è necessario:
- essere in possesso di un API Key per l’utilizzo delle mappe su Android e su iOS;
- impostare i permessi per iOS e Android.
Per ottenere la API Key e abilitare il servizio di Google Maps è necessario:
- andare sulla Google Developers Console;
- creare o scegliere il progetto su cui si desidera abilitare Google Maps;
- selezionare il menu di navigazione e quindi selezionare la voce Google Maps nella sezione Altre Soluzioni Google;
- nel menù a tendina che compare selezionare la voce API;
- per abilitare Google Maps per Android, selezionare Maps SDK per Android nella sezione API aggiuntive, quindi selezionare ENABLE;
- per abilitare Google Maps per iOS, selezionare Maps SDK per iOS nella sezione API aggiuntive, quindi selezionare ENABLE.
A questo punto se abbiamo seguito correttamente tutti i passi, troveremo le nostre API nella nella sezione API abilitate e non ci resta che abilitare le credenziali.
Spostiamoci nella sezione Api e servizi e clicchiamo sulla voce Credenziali.
Nella nuova sezione, clicchiamo sulla voce CREA CREDENZIALI per ottenere in pochi istanti una nuova chiave da utilizzare nella nostra app.
Ai fini di questa lezione non è necessario applicare alcun tipo di restrizione per impedirne l’uso, ma è caldamente consigliato farlo per i rilasci delle applicazioni in produzione.
Integriamo la nostra API Key nel progetto.
Per iOS è necessario aggiungere nel file ios/Runner/AppDelegate.swift l’import a GoogleMaps
e nel corpo di Bool
le nostre API key come segue.
import GoogleMaps
@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
GMSServices.provideAPIKey("YOUR KEY HERE")
GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
}
Successivamente, aggiungiamo nel file ios/Runner/Info.plist quanto segue.
<key>io.flutter.embedded_views_preview</key>
<string>YES</string>
Per Android, apriamo il file android/app/src/main/AndroidManifest.xml e aggiungiamo all’interno del tag <application>
<meta-data android:name="com.google.android.geo.API_KEY"
android:value="YOUR API KEY"/>
Sia per Android che per iOS, andiamo a sostituire la stringa YOUR API KEY
con la chiave ottenuta dalla Google Developer Console.
Oltre a questi permessi, aggiungiamo anche quelli inerenti alla localizzazione, come indicato nella lezione precedente, per mostrare la posizione corrente dell’utente sulla mappa.
Aggiungere la mappa
Partiamo con il primo aspetto: integrare le mappe nella nostra app.
Apriamo il file screen1.dart e creiamo la classe Stateful Screen1
come segue.
class Screen1 extends StatefulWidget {
@override
State<Screen1> createState() => Screen1State();
}
class Screen1State extends State<Screen1> {
@override
Widget build(BuildContext context) {
return //...
}
}
Ora che abbiamo definito lo stato del nostro widget possiamo definire:
- un oggetto
Completer
di tipoGoogleMapController
che permette di definire un controller per ogni istanza diGoogleMap
in esecuzione sul dispositivo; - un oggetto
LatLng
rappresentate il Colosseo; - la posizione iniziale della mappa da mostrare all’utente sfruttando la classe
CameraPosition
che permette di definire un punto geografico (LatLng
) e un livello di zoom; - la mappa utilizzando il widget
GoogleMap
.
Per farlo, ci basteranno poche e semplici righe di codice. Vediamo un esempio di implementazione del nostro Screen1State
.
class Screen1State extends State<Screen1> {
Completer<GoogleMapController> _controller = Completer();
static final LatLng colosseoLatLng = LatLng(41.890251, 12.492373);
static final CameraPosition colosseo = CameraPosition(
target: LatLng(41.890251, 12.492373),
zoom: 16.5,
);
@override
Widget build(BuildContext context) {
return new Scaffold(
body: GoogleMap(
mapType: MapType.normal,
initialCameraPosition: colosseo,
onMapCreated: (GoogleMapController controller) {
_controller.complete(controller);
},
),
);
}
}
Possiamo vedere che per il widget GoogleMap
abbiamo impostato:
- il tipo di mappa da utilizzare tramite la proprietà
mapType
; - la proprietà
initialCameraPosition
a cui abbiamo impostato l’oggettocolosseo
che rappresenta la posizione desiderata per la camera; - la
onMapCreated
in cui abbiamo associato all’oggetto_controller
la nuova istanza diGoogleMapController
ottenuta con l’inizializzazione del widgetGoogleMap
.
Eseguiamo l’applicazione per ottenere il seguente risultato.
Aggiungiamo adesso:
- un marker per indicare il punto esatto in cui si trova il Colosseo, fornendo per quel marker un titolo e uno snippet;
- un percorso breve identificato da un linea verde che rappresenta i fori imperiali.
Per farlo, basterà utilizzare le classi Marker
e Polyline
, rispettivamente.
Vediamo come con un semplice esempio.
static final Marker colosseoMarker = Marker(
markerId: MarkerId(colosseoLatLng.toString()),
position: colosseoLatLng,
infoWindow: InfoWindow(
title: 'Colosseo',
snippet: 'Roma, Italy',
),
icon: BitmapDescriptor.defaultMarker,
);
static final Polyline foriImperiali = Polyline(
polylineId: PolylineId(colosseoLatLng.toString()),
visible: true,
width: 7,
color: Colors.green,
points: [
LatLng(41.891456, 12.490189),
LatLng(41.891436, 12.490261),
LatLng(41.891348, 12.490476),
LatLng(41.891300, 12.490733),
LatLng(41.891260, 12.490911)
],
);
A questo punto, modifichiamo il widget GoogleMap
per aggiungere le proprietà polylines
e markers
che ci permetteranno di impostare la Polyline
e il Marker
definiti.
Widget build(BuildContext context) {
return new Scaffold(
body: GoogleMap(
// . . .
polylines: <Polyline>{foriImperiali},
markers: <Marker>{colosseoMarker},
onMapCreated: (GoogleMapController controller) {
_controller.complete(controller);
},
),
);
}
In questo caso, avendo una sola Polyline
e un solo Marker
creati in modo statico e sempre fruibili non abbiamo gestito il relativo stato tramite il metodo setState()
. In un'applicazione reale è importante non dimenticarsene.
Eseguiamo quindi la nostra applicazione ottenendo il risultato seguente.
Infine, non resta che la parte più facile. Mostrare la posizione dell’utente sulla mappa. Il metodo più semplice che non coinvolge l’utilizzo del plugin geolocator è quello di impostare a true
le proprietà myLocationButtonEnabled
e myLocationEnabled
del widget GoogleMap
.
Il widget GoogleMap
sarà quindi il seguente.
Widget build(BuildContext context) {
return new Scaffold(
body: GoogleMap(
// . . .
myLocationButtonEnabled: true,
myLocationEnabled: true,
onMapCreated: (GoogleMapController controller) {
_controller.complete(controller);
},
),
);
}
Eseguiamo l’applicazione per vedere i risultati delle nostre modifiche.
Come possiamo vedere dalle immagini, la posizione dell’utente sarà rappresentata dal pallino blu.