1. Introduzione
Flutter è l'SDK per app mobile di Google per creare esperienze native di alta qualità su iOS e Android in tempi record.
Con il plug-in Google Maps Flutter, puoi aggiungere mappe basate sui dati di Google Maps alla tua applicazione. Il plug-in gestisce automaticamente l'accesso ai server di Google Maps, la visualizzazione della mappa e la risposta ai gesti dell'utente, come clic e trascinamenti. Puoi anche aggiungere indicatori alla mappa. Questi oggetti forniscono informazioni aggiuntive per le posizioni sulla mappa e consentono all'utente di interagire con la mappa.
Cosa creerai
In questo codelab, creerai un'app mobile con una mappa di Google utilizzando l'SDK Flutter. La tua app sarà in grado di:
|
|
Che cos'è Flutter?
Flutter ha tre funzionalità principali.
- Sviluppo rapido: crea le tue applicazioni Android e iOS in millisecondi con il ricaricamento rapido con stato.
- Espressivo e flessibile: distribuisci rapidamente le funzionalità concentrandoti sulle esperienze native degli utenti finali.
- Prestazioni native sia su iOS che su Android: i widget di Flutter incorporano tutte le differenze critiche della piattaforma, come scorrimento, navigazione, icone e caratteri, per offrire prestazioni native complete.
Google Maps ha:
- Copertura del 99% del mondo: crea le mappe con dati completi e affidabili di oltre 200 paesi e aree geografiche.
- 25 milioni di aggiornamenti al giorno: sfrutta dati sulla posizione precisi e in tempo reale.
- 1 miliardo di utenti attivi mensili: espanditi su larga scala in modo rapido e senza preoccupazioni, grazie all'infrastruttura di Google Maps.
Questo codelab ti guida nella creazione di un'esperienza Google Maps in un'app Flutter sia per iOS che per Android.
Cosa imparerai a fare
- Come creare una nuova applicazione Flutter.
- Come configurare un plug-in Flutter di Google Maps.
- Come aggiungere indicatori a una mappa utilizzando i dati sulla posizione di un servizio web.
Questo codelab si concentra sull'aggiunta di una mappa di Google a un'app Flutter. Concetti e blocchi di codice non pertinenti sono trattati solo superficialmente e sono forniti solo per operazioni di copia e incolla.
Che cosa ti piacerebbe imparare da questo codelab?
2. Configurare l'ambiente Flutter
Per completare questo laboratorio, hai bisogno di due software: l'SDK Flutter e un editor. Questo codelab presuppone l'utilizzo di Android Studio, ma puoi utilizzare l'editor che preferisci.
Puoi eseguire questo codelab utilizzando uno dei seguenti dispositivi:
- Un dispositivo fisico (Android o iOS) connesso al computer e impostato sulla modalità sviluppatore.
- Il simulatore iOS. (richiede l'installazione degli strumenti Xcode).
- L'emulatore Android. (Richiede la configurazione in Android Studio.)
3. Per iniziare
Guida introduttiva a Flutter
Il modo più semplice per iniziare a utilizzare Flutter è usare lo strumento a riga di comando flutter per creare tutto il codice necessario per un'esperienza di avvio semplice.
$ flutter create google_maps_in_flutter --platforms android,ios,web Creating project google_maps_in_flutter... Resolving dependencies in `google_maps_in_flutter`... Downloading packages... Got dependencies in `google_maps_in_flutter`. Wrote 81 files. All done! You can find general documentation for Flutter at: https://docs.flutter.dev/ Detailed API documentation is available at: https://api.flutter.dev/ If you prefer video documentation, consider: https://www.youtube.com/c/flutterdev In order to run your application, type: $ cd google_maps_in_flutter $ flutter run Your application code is in google_maps_in_flutter/lib/main.dart.
Aggiungere il plug-in Google Maps Flutter come dipendenza
Aggiungere funzionalità aggiuntive a un'app Flutter è facile utilizzando i pacchetti Pub. In questo codelab introduci il plug-in Flutter di Google Maps eseguendo il seguente comando dalla directory del progetto.
$ cd google_maps_in_flutter $ flutter pub add google_maps_flutter Resolving dependencies... Downloading packages... + csslib 1.0.0 + flutter_plugin_android_lifecycle 2.0.19 + flutter_web_plugins 0.0.0 from sdk flutter + google_maps 7.1.0 + google_maps_flutter 2.6.1 + google_maps_flutter_android 2.8.0 + google_maps_flutter_ios 2.6.0 + google_maps_flutter_platform_interface 2.6.0 + google_maps_flutter_web 0.5.7 + html 0.15.4 + js 0.6.7 (0.7.1 available) + js_wrapping 0.7.4 leak_tracker 10.0.4 (10.0.5 available) leak_tracker_flutter_testing 3.0.3 (3.0.5 available) material_color_utilities 0.8.0 (0.11.1 available) meta 1.12.0 (1.14.0 available) + plugin_platform_interface 2.1.8 + sanitize_html 2.1.0 + stream_transform 2.1.0 test_api 0.7.0 (0.7.1 available) + web 0.5.1 Changed 16 dependencies! 6 packages have newer versions incompatible with dependency constraints. Try `flutter pub outdated` for more information.
Configurazione di iOS platform
Per ottenere l'ultima versione dell'SDK Google Maps su iOS è necessaria una versione minima della piattaforma iOS 14. Modifica la parte superiore del file di configurazione ios/Podfile nel seguente modo.
ios/Podfile
# Google Maps SDK requires platform version 14
# https://developers.google.com/maps/flutter-package/config#ios
platform :ios, '14.0'
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
Configurazione di Android minSDK
Per utilizzare Google Maps SDK su Android è necessario impostare minSdk su 21. Modifica il file di configurazione android/app/build.gradle come segue.
android/app/build.gradle
android {
defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId = "com.example.google_maps_in_flutter"
// Minimum Android version for Google Maps SDK
// https://developers.google.com/maps/flutter-package/config#android
minSdk = 21
targetSdk = flutter.targetSdkVersion
versionCode = flutterVersionCode.toInteger()
versionName = flutterVersionName
}
}
4. Aggiungere Google Maps all'app
Tutto dipende dalle chiavi API
Per utilizzare Google Maps nella tua app Flutter, devi configurare un progetto API con Google Maps Platform, seguendo le istruzioni riportate in Utilizzo della chiave API di Maps SDK for Android, Utilizzo della chiave API di Maps SDK for iOS e Utilizzo della chiave API dell'API Maps JavaScript. Con le chiavi API a portata di mano, segui questi passaggi per configurare le applicazioni Android e iOS.
Aggiungere una chiave API per un'app per Android
Per aggiungere una chiave API all'app per Android, modifica il file AndroidManifest.xml in android/app/src/main. Aggiungi una singola voce meta-data contenente la chiave API creata nel passaggio precedente all'interno del nodo application.
android/app/src/main/AndroidManifest.xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application
android:label="google_maps_in_flutter"
android:name="${applicationName}"
android:icon="@mipmap/ic_launcher">
<!-- TODO: Add your Google Maps API key here -->
<meta-data android:name="com.google.android.geo.API_KEY"
android:value="YOUR-KEY-HERE"/>
<activity
android:name=".MainActivity"
android:exported="true"
android:launchMode="singleTop"
android:taskAffinity=""
android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize">
<!-- Specifies an Android theme to apply to this Activity as soon as
the Android process has started. This theme is visible to the user
while the Flutter UI initializes. After that, this theme continues
to determine the Window background behind the Flutter UI. -->
<meta-data
android:name="io.flutter.embedding.android.NormalTheme"
android:resource="@style/NormalTheme"
/>
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<!-- Don't delete the meta-data below.
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
<meta-data
android:name="flutterEmbedding"
android:value="2" />
</application>
<!-- Required to query activities that can process text, see:
https://developer.android.com/training/package-visibility and
https://developer.android.com/reference/android/content/Intent#ACTION_PROCESS_TEXT.
In particular, this is used by the Flutter engine in io.flutter.plugin.text.ProcessTextPlugin. -->
<queries>
<intent>
<action android:name="android.intent.action.PROCESS_TEXT"/>
<data android:mimeType="text/plain"/>
</intent>
</queries>
</manifest>
Aggiungere una chiave API per un'app per iOS
Per aggiungere una chiave API all'app per iOS, modifica il file AppDelegate.swift in ios/Runner. A differenza di Android, l'aggiunta di una chiave API su iOS richiede modifiche al codice sorgente dell'app Runner. AppDelegate è il singleton principale che fa parte del processo di inizializzazione dell'app.
Apporta due modifiche a questo file. Innanzitutto, aggiungi un'istruzione #import per inserire le intestazioni di Google Maps, quindi chiama il metodo provideAPIKey() del singleton GMSServices. Questa chiave API consente a Google Maps di pubblicare correttamente i riquadri della mappa.
ios/Runner/AppDelegate.swift
import Flutter
import UIKit
import GoogleMaps // Add this import
@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
GeneratedPluginRegistrant.register(with: self)
// TODO: Add your Google Maps API key
GMSServices.provideAPIKey("YOUR-API-KEY") // Add this line
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
}
Aggiungere una chiave API per un'app web
Per aggiungere una chiave API all'app web, modifica il file index.html in web. Aggiungi un riferimento allo script JavaScript di Maps nella sezione head, con la tua chiave API.
web/index.html
<!DOCTYPE html>
<html>
<head>
<!--
If you are serving your web app in a path other than the root, change the
href value below to reflect the base path you are serving from.
The path provided below has to start and end with a slash "/" in order for
it to work correctly.
For more details:
* https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base
This is a placeholder for base href that will be replaced by the value of
the `--base-href` argument provided to `flutter build`.
-->
<base href="$FLUTTER_BASE_HREF">
<meta charset="UTF-8">
<meta content="IE=Edge" http-equiv="X-UA-Compatible">
<meta name="description" content="A new Flutter project.">
<!-- iOS meta tags & icons -->
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<meta name="apple-mobile-web-app-title" content="google_maps_in_flutter">
<link rel="apple-touch-icon" href="icons/Icon-192.png">
<!-- Favicon -->
<link rel="icon" type="image/png" href="favicon.png"/>
<!-- Add your Google Maps API key here -->
<script src="https://maps.googleapis.com/maps/api/js?key=YOUR-KEY-HERE"></script>
<title>google_maps_in_flutter</title>
<link rel="manifest" href="manifest.json">
</head>
<body>
<script src="flutter_bootstrap.js" async></script>
</body>
</html>
Visualizzare una mappa sullo schermo
Ora è il momento di visualizzare una mappa sullo schermo. Sostituisci i contenuti di lib/main.dart con quanto segue.
lib/main.dart
import 'package:flutter/material.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatefulWidget {
const MyApp({super.key});
@override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
late GoogleMapController mapController;
final LatLng _center = const LatLng(45.521563, -122.677433);
void _onMapCreated(GoogleMapController controller) {
mapController = controller;
}
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(
useMaterial3: true,
colorSchemeSeed: Colors.green[700],
),
home: Scaffold(
appBar: AppBar(
title: const Text('Maps Sample App'),
elevation: 2,
),
body: GoogleMap(
onMapCreated: _onMapCreated,
initialCameraPosition: CameraPosition(
target: _center,
zoom: 11.0,
),
),
),
);
}
}
Esecuzione dell'app
Esegui l'app Flutter in iOS o Android per visualizzare una singola visualizzazione della mappa, centrata su Portland. In alternativa, esegui un emulatore Android o un simulatore iOS. Puoi modificare il centro della mappa per rappresentare la tua città o un luogo importante per te.
$ flutter run
|
|
5. Metti Google sulla mappa
Google ha molti uffici in tutto il mondo, dal Nord America, all'America Latina, all'Europa, all'Asia Pacifico, fino all'Africa e al Medio Oriente. La cosa interessante di queste mappe, se le esamini, è che hanno un endpoint API facilmente utilizzabile per fornire le informazioni sulla sede dell'ufficio in formato JSON. In questo passaggio, inserisci le sedi degli uffici sulla mappa. In questo passaggio, utilizzerai la generazione di codice per analizzare JSON.
Aggiungi al progetto tre nuove dipendenze Flutter come segue. Aggiungi il pacchetto http per effettuare facilmente richieste HTTP, i pacchetti json_serializable e json_annotation per dichiarare la struttura degli oggetti per rappresentare i documenti JSON e aggiungi build_runner per il supporto della generazione di codice.
$ flutter pub add http json_annotation json_serializable dev:build_runner Resolving dependencies... Downloading packages... + _fe_analyzer_shared 67.0.0 (68.0.0 available) + analyzer 6.4.1 (6.5.0 available) + args 2.5.0 + build 2.4.1 + build_config 1.1.1 + build_daemon 4.0.1 + build_resolvers 2.4.2 + build_runner 2.4.9 + build_runner_core 7.3.0 + built_collection 5.1.1 + built_value 8.9.2 + checked_yaml 2.0.3 + code_builder 4.10.0 + convert 3.1.1 + crypto 3.0.3 + dart_style 2.3.6 + file 7.0.0 + fixnum 1.1.0 + frontend_server_client 4.0.0 + glob 2.1.2 + graphs 2.3.1 + http 1.2.1 + http_multi_server 3.2.1 + http_parser 4.0.2 + io 1.0.4 js 0.6.7 (0.7.1 available) + json_annotation 4.9.0 + json_serializable 6.8.0 leak_tracker 10.0.4 (10.0.5 available) leak_tracker_flutter_testing 3.0.3 (3.0.5 available) + logging 1.2.0 material_color_utilities 0.8.0 (0.11.1 available) meta 1.12.0 (1.14.0 available) + mime 1.0.5 + package_config 2.1.0 + pool 1.5.1 + pub_semver 2.1.4 + pubspec_parse 1.2.3 + shelf 1.4.1 + shelf_web_socket 1.0.4 + source_gen 1.5.0 + source_helper 1.3.4 test_api 0.7.0 (0.7.1 available) + timing 1.0.1 + typed_data 1.3.2 + watcher 1.1.0 + web_socket_channel 2.4.5 + yaml 3.1.2 Changed 42 dependencies! 8 packages have newer versions incompatible with dependency constraints. Try `flutter pub outdated` for more information.
Analisi JSON con generazione di codice
Potresti aver notato che i dati JSON restituiti dall'endpoint API hanno una struttura regolare. Sarebbe utile generare il codice per organizzare i dati in oggetti che puoi utilizzare nel codice.
Nella directory lib/src, crea un file locations.dart e descrivi la struttura dei dati JSON restituiti nel seguente modo:
lib/src/locations.dart
import 'dart:convert';
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart' show rootBundle;
import 'package:http/http.dart' as http;
import 'package:json_annotation/json_annotation.dart';
part 'locations.g.dart';
@JsonSerializable()
class LatLng {
LatLng({
required this.lat,
required this.lng,
});
factory LatLng.fromJson(Map<String, dynamic> json) => _$LatLngFromJson(json);
Map<String, dynamic> toJson() => _$LatLngToJson(this);
final double lat;
final double lng;
}
@JsonSerializable()
class Region {
Region({
required this.coords,
required this.id,
required this.name,
required this.zoom,
});
factory Region.fromJson(Map<String, dynamic> json) => _$RegionFromJson(json);
Map<String, dynamic> toJson() => _$RegionToJson(this);
final LatLng coords;
final String id;
final String name;
final double zoom;
}
@JsonSerializable()
class Office {
Office({
required this.address,
required this.id,
required this.image,
required this.lat,
required this.lng,
required this.name,
required this.phone,
required this.region,
});
factory Office.fromJson(Map<String, dynamic> json) => _$OfficeFromJson(json);
Map<String, dynamic> toJson() => _$OfficeToJson(this);
final String address;
final String id;
final String image;
final double lat;
final double lng;
final String name;
final String phone;
final String region;
}
@JsonSerializable()
class Locations {
Locations({
required this.offices,
required this.regions,
});
factory Locations.fromJson(Map<String, dynamic> json) =>
_$LocationsFromJson(json);
Map<String, dynamic> toJson() => _$LocationsToJson(this);
final List<Office> offices;
final List<Region> regions;
}
Future<Locations> getGoogleOffices() async {
const googleLocationsURL = 'https://about.google/static/data/locations.json';
// Retrieve the locations of Google offices
try {
final response = await http.get(Uri.parse(googleLocationsURL));
if (response.statusCode == 200) {
return Locations.fromJson(
json.decode(response.body) as Map<String, dynamic>);
}
} catch (e) {
if (kDebugMode) {
print(e);
}
}
// Fallback for when the above HTTP request fails.
return Locations.fromJson(
json.decode(
await rootBundle.loadString('assets/locations.json'),
) as Map<String, dynamic>,
);
}
Una volta aggiunto questo codice, l'IDE (se ne utilizzi uno) dovrebbe mostrare alcune linee rosse ondulate, in quanto fa riferimento a un file fratello inesistente, locations.g.dart. Questo file generato esegue la conversione tra strutture JSON non tipizzate e oggetti denominati. Crealo eseguendo build_runner nel seguente modo:
$ dart run build_runner build --delete-conflicting-outputs [INFO] Generating build script... [INFO] Generating build script completed, took 357ms [INFO] Creating build script snapshot...... [INFO] Creating build script snapshot... completed, took 10.5s [INFO] There was output on stdout while compiling the build script snapshot, run with `--verbose` to see it (you will need to run a `clean` first to re-snapshot). [INFO] Initializing inputs [INFO] Building new asset graph... [INFO] Building new asset graph completed, took 646ms [INFO] Checking for unexpected pre-existing outputs.... [INFO] Deleting 1 declared outputs which already existed on disk. [INFO] Checking for unexpected pre-existing outputs. completed, took 3ms [INFO] Running build... [INFO] Generating SDK summary... [INFO] 3.4s elapsed, 0/3 actions completed. [INFO] Generating SDK summary completed, took 3.4s [INFO] 4.7s elapsed, 2/3 actions completed. [INFO] Running build completed, took 4.7s [INFO] Caching finalized dependency graph... [INFO] Caching finalized dependency graph completed, took 36ms [INFO] Succeeded after 4.8s with 2 outputs (7 actions)
Ora l'analisi del codice dovrebbe essere di nuovo pulita. Successivamente, aggiungiamo il file di riserva locations.json utilizzato nella funzione getGoogleOffices. Uno dei motivi per cui è incluso questo fallback è che i dati statici caricati in questa funzione vengono pubblicati senza intestazioni CORS e pertanto non verranno caricati in un browser web. Le app Flutter per Android e iOS non richiedono intestazioni CORS, ma l'accesso ai dati mobili può essere complicato anche nelle migliori condizioni.
Vai a https://about.google/static/data/locations.json nel browser e salva i contenuti nella directory degli asset. In alternativa, puoi utilizzare la riga di comando come segue.
$ mkdir assets
$ cd assets
$ curl -o locations.json https://about.google/static/data/locations.json
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 30348 100 30348 0 0 75492 0 --:--:-- --:--:-- --:--:-- 75492
Ora che hai scaricato il file dell'asset, aggiungilo alla sezione Flutter del file pubspec.yaml.
pubspec.yaml
flutter:
uses-material-design: true
assets:
- assets/locations.json
Modifica il file main.dart per richiedere i dati della mappa, quindi utilizza le informazioni restituite per aggiungere uffici alla mappa:
lib/main.dart
import 'package:flutter/material.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'src/locations.dart' as locations;
void main() {
runApp(const MyApp());
}
class MyApp extends StatefulWidget {
const MyApp({super.key});
@override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
final Map<String, Marker> _markers = {};
Future<void> _onMapCreated(GoogleMapController controller) async {
final googleOffices = await locations.getGoogleOffices();
setState(() {
_markers.clear();
for (final office in googleOffices.offices) {
final marker = Marker(
markerId: MarkerId(office.name),
position: LatLng(office.lat, office.lng),
infoWindow: InfoWindow(
title: office.name,
snippet: office.address,
),
);
_markers[office.name] = marker;
}
});
}
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(
useMaterial3: true,
colorSchemeSeed: Colors.green[700],
),
home: Scaffold(
appBar: AppBar(
title: const Text('Google Office Locations'),
elevation: 2,
),
body: GoogleMap(
onMapCreated: _onMapCreated,
initialCameraPosition: const CameraPosition(
target: LatLng(0, 0),
zoom: 2,
),
markers: _markers.values.toSet(),
),
),
);
}
}
Questo codice esegue diverse operazioni:
- In
_onMapCreated, utilizza il codice di analisi JSON del passaggio precedente,awaiting fino al caricamento. Utilizza poi i dati restituiti per creareMarkerall'interno di un callbacksetState(). Una volta che l'app riceve nuovi indicatori, i flag setState indicano a Flutter di ridisegnare lo schermo, causando la visualizzazione delle sedi degli uffici. - I marcatori sono archiviati in un
Mapassociato al widgetGoogleMap. In questo modo, gli indicatori vengono collegati alla mappa corretta. Naturalmente, puoi avere più mappe e visualizzare indicatori diversi in ognuna.

Ecco uno screenshot di ciò che hai ottenuto. A questo punto, è possibile apportare molte aggiunte interessanti. Ad esempio, potresti aggiungere una visualizzazione elenco degli uffici che sposta e ingrandisce la mappa quando l'utente fa clic su un ufficio, ma, come si dice, questo esercizio è lasciato al lettore.
6. Passaggi successivi
Complimenti!
Hai completato il codelab e creato un'app Flutter con una mappa di Google. Hai anche interagito con un servizio web JSON.
Altri passaggi successivi
Questo codelab ha creato un'esperienza per visualizzare un numero di punti su una mappa. Esistono diverse app mobile che si basano su questa funzionalità per soddisfare le esigenze di molti utenti diversi. Esistono altre risorse che possono aiutarti ad approfondire l'argomento:
- Build Mobile Apps With Flutter and Google Maps (un intervento tenuto a Cloud Next 2019)
- Il pacchetto
google_maps_webservicedi Hadrien Lejard, che rende molto semplice l'utilizzo dei servizi web di Google Maps, come l'API Directions, l'API Distance Matrix e l'API Places. - Se vuoi esaminare diverse opzioni per l'utilizzo di un'API tramite JSON REST, consulta il post di Andrew Brogdon su Medium per una serie di opzioni per lavorare con le API JSON REST.

