Aplicacions amb Flutter, Dart i Flame

Tutorial Flutter Flame Projectes   Recursos CITCEA
Exemples Dart Dades pràctiques     Inici

Google Maps

Hi ha un giny que ens permet mostrar maper de Google Maps, modificar-ne detalls i interaccionar amb ells.

Configuració

Per poder fer servir els mapes de Google Maps, caldrà afegir el paquet Google Maps for Flutter. Obrirem el fitxer pubspec.yaml i afegirem una línia a l'apartat dependencies, on indicarem que volem fer servir aquest paquet en versions iguals o superiors a l'actual.

dependencies:
  flutter:
    sdk: flutter
  google_maps_flutter: ^2.13.1

En el moment de crear aquest exemple, la darrera versió era la 2.13.1; però convé posar-hi la darrera existent en el moment de crear la nostra aplicació. A la pàgina d'informació sobre Google Maps for Flutter hi trobarem quina és la darrera versió en aquell moment. Un cop modificat el fitxer, cal picar al botó descarregar per descarregar els nous paquets.

Per poder emprar els mapes de Google, ens cal tenir un identificador. Hem d'anar a Google Cloud, on ens haurem d'identificar amb el nostre compte de Google.

Nota: En algunes aplicacions ens hem trobat que els comptes de Google vinculats als usuaris UPC tenen algunes restriccions. Pot ser una bona idea fer servir el nostre compte personal de Google.

A la part superior dreta de la pàgina, picarem el botó Consola. Un cop a la consola, a la part superior esquerra podem tenir el nom del projecte actual o bé l'opció de crear un nou projecte. Si ens surt el nom del projecte actual, picarem a sobre i veurem la llista dels nostres projectes i un botó per crear un projecte nou. En aquest cas, creem un nou projecte. Ens demanarà que li donem un nom i, per exemple, hi posarem MapaFlutter, sense indicar cap organització. Un cop fet això, hem de seleccionar el projecte que acabem de crear.

Ja dins del projecte, picarem l'enllaç Panel i cercarem (a l'esquerra) l'opció Explorar y habilitar API. A la part superior cercarem el botó Habilitar APIs y servicios. Hi ha moltes API disponibles, però ens interessen les de mapes; per tant, cerquem aquest títol. Activarem les opcions Maps SDK for Android i Maps SDK for iOS. Pot ser que ens demani que definim un compte de facturació; si és el cas, picarem a cancel·lar perquè no ho necessitem.

Tornem enrere i estarem a la pàgina APIs y servicios (si no és així, piquem al botó de les tres ratlles de la part superior esquerra i cerquem aquesta opció). En el menú de l'esquerra, piquem a l'opció Credenciales. A la part superior picarem el botó Crear credenciales i triarem l'opció Clave de API. Aquí podríem establir restriccions però, en principi, no en posarem cap. Llavors picarem el botó Crear. Un cop fet això, veurem la clau, que pot tenir un aspecte com aquest:

^^1aqOrZiWSnXDcQmWQSAIzaSyDAzkeZb4WynCw

i picarem el botó Cerrar. Ara hem d'introduir aquesta clau a les configuracions d'Android i iOS del nostre projecte. Òbviament, si només volem fer l'aplicació per a un sistema operatiu, podem prescindir de l'altre.

Per a Android, cal anar al fitxer AndroidManifest.xml que es troba a la carpeta main de la carpeta src de la carpeta app de la carpeta android i afegir la línia del permís d'internet després de la de manifest i abans de la d'application. Al final de l'etiqueta application, abans de tancar-la, posarem les instruccions que indiquen la clau.

<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    <uses-permission android:name="android.permission.INTERNET" />
    <application
...
        <meta-data
            android:name="com.google.android.geo.API_KEY"
            android:value="^^1aqOrZiWSnXDcQmWQSAIzaSyDAzkeZb4WynCw" />
    </application>
...

Per a iOS cal anar al fitxer AppDelegate.swift que es troba a la carpeta Runner de la carpeta ios i afegir la importació de Google Maps al final de la llista d'importacions. També hem d'afegir la clau, que posarem a l'inici del contingut de Bool.

...
import GoogleMaps
...
  ) -> Bool {
    GMSServices.provideAPIKey("^^1aqOrZiWSnXDcQmWQSAIzaSyDAzkeZb4WynCw")
...

Per a iOS també cal anar al fitxer Info.plist que es troba a la carpeta Runner de la carpeta ios i afegir el missatge amb el qual es demanarà el permís a l'usuari. Afegirem dues línies després de l'etiqueta <dict> i abans de la resta del seu comtingut.

...
<dict>
	<key>NSLocationWhenInUseUsageDescription</key>
	<string>Cal accés a la teva ubicació per mostrar el mapa</string>
...

on el missatges es pot personalitzar segons ens agradi.

En el cas d'Android, ens poden aparèixer problemes deguts a versions antigues. Si no ens diu res, no cal tocar-ho però pot passar que haguem de modificar el fitxer settings.gradle.kts amb la versió que ens recomani.

...
plugins {
    ...
    id("com.android.application") version "8.7.3" apply false
    ...
}
...

I una cosa similar amb el fitxer gradle-wrapper.properties, que es troba a la carpeta wrapper que està dins de la carpeta gradle.

...
distributionUrl=https\://services.gradle.org/distributions/

gradle-8.12-all

.zip ...

Mapa bàsic

Per mostrar un mapa, cal fer servir el giny GoogleMap. Sempre que es mostra un mapa, cal indicar la posició on es centrarà la vista. Opcionalment també podem indicar un valor de zoom; és molt convenient fer-ho, si no volem que ens mostri un mapa vist des de molt lluny.

...
class _PantPrincipalState extends State<PantPrincipal> {
  final CameraPosition _posicioInicial = CameraPosition(
    target: LatLng(41.384929, 2.115582),
    zoom: 17
  );

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("Mapa")),
      body: GoogleMap(initialCameraPosition: _posicioInicial),
    );
  }
}

La comanda mapType ens permet triar quin tipus de mapa volem mostrar. El valor per defecte és normal, que mostra el dibuix del mapa. L'opció satellite ens mostra una vista aèria i hybrid una vista aèria amb indicació dels carrers.

Marcadors

Es poden posar marcadors sobre el mapa. Els marcadors estaran en un conjunt de dades (set) i, per a cada un, definirem un identificador únic, unes coordenades i un títol. Aquest títol es mostrarà quan piquem sobre el marcador en el mapa.

...
class _PantPrincipalState extends State<PantPrincipal> {
  final CameraPosition _posicioInicial = CameraPosition(
    target: LatLng(41.382865, 2.1153599),
    zoom: 16
  );
  final Set<Marker> _marcadors = {
    Marker(
      markerId: MarkerId("ETSEIB"), 
      position: LatLng(41.384929, 2.115582),
      infoWindow: InfoWindow(title: "ETSEIB")
    ),
    Marker(
      markerId: MarkerId("FME"), 
      position: LatLng(41.383522, 2.115755),
      infoWindow: InfoWindow(title: "FME")
    ),
  };

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("Mapa")),
      body: GoogleMap(
        initialCameraPosition: _posicioInicial,
        mapType: MapType.satellite,
        markers: _marcadors,
      ),
    );
  }
}

Podem posar marcadors creats separadament, però sempre els haurem de posar entre claus.

...
class _PantPrincipalState extends State {
  final Marker facFisica = const Marker(
    markerId: MarkerId('facFisica'),
    position: LatLng(41.385246, 2.116744),
    infoWindow: InfoWindow(title: "Facultat de Física")
  );
  final CameraPosition _posicioInicial = CameraPosition(
    target: LatLng(41.382865, 2.1153599),
    zoom: 16
  );

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("Mapa")),
      body: GoogleMap(
        initialCameraPosition: _posicioInicial,
        mapType: MapType.satellite,
        markers: {facFisica},
      ),
    );
  }
}

Es pot canviar el color de cada marcador:

...
  final Marker facFisica = Marker(
    markerId: MarkerId('facFisica'),
    position: LatLng(41.385246, 2.116744),
    icon: BitmapDescriptor.defaultMarkerWithHue(BitmapDescriptor.hueCyan),
    infoWindow: InfoWindow(title: "Facultat de Física")
  );
...

Interacció de l'usuari

Els mapes tenen una comanda onTap que permet executar una funció quan l'usuari pica en una posició del mapa; la posició on ha picat està disponible per passar com a paràmetre a la funció.

...
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("Mapa")),
      body: GoogleMap(
        initialCameraPosition: _posicioInicial,
        mapType: MapType.satellite,
        markers: _marcadors,
        onTap: (posicio) => posaMarcador(posicio),
      ),
    );
  }
}

 

 

 

 

 

 

 

 

 

 

Llicència de Creative Commons
Aquesta obra d'Oriol Boix està llicenciada sota una llicència no importada Reconeixement-NoComercial-SenseObraDerivada 3.0.