1. Introduction
Flutter est le SDK d'applications mobiles de Google. Il permet de créer des expériences natives de haute qualité sur iOS et Android en un temps record.
Avec le plug-in Flutter pour Google Maps, vous pouvez ajouter des cartes à votre application à partir de données Google Maps. Le plug-in gère automatiquement l'accès aux serveurs Google Maps, l'affichage des cartes et la réponse aux gestes des utilisateurs (par exemple lorsqu'ils cliquent sur la carte ou la font glisser). Vous pouvez également y ajouter des repères. Ces objets fournissent des informations supplémentaires concernant les lieux affichés sur la carte et permettent à l'utilisateur d'interagir avec elle.
Objectifs de l'atelier
Dans cet atelier de programmation, vous allez créer une application mobile contenant une carte Google Maps à l'aide du SDK Flutter. Cette application pourra :
|
Qu'est-ce que Flutter ?
Flutter présente trois principaux atouts.
- Développement rapide : créez vos applications Android et iOS en quelques millisecondes avec le hot reload avec état.
- Clair et souple : proposez rapidement des fonctionnalités axées sur les expériences natives de l'utilisateur final.
- Performances natives sur iOS et Android : les widgets de Flutter intègrent toutes les différences majeures entre plate-formes (comme le défilement, la navigation, les icônes et les polices) afin d'offrir des performances natives optimales.
Quelques chiffres concernant Google Maps :
- 99 % de couverture mondiale : créez des applications avec des données fiables et complètes pour plus de 200 pays et territoires.
- 25 millions de mises à jour quotidiennes : bénéficiez d'informations de position en temps réel exactes.
- 1 milliard d'utilisateurs actifs par mois : évoluez en toute confiance grâce à l'infrastructure de Google Maps.
Cet atelier de programmation vous explique comment créer une expérience Google Maps dans une application Flutter pour iOS et Android.
Points abordés
- Apprendre à créer une application Flutter
- Configurer un plug-in Flutter pour Google Maps
- Ajouter des repères à une carte à l'aide des données de localisation d'un service Web
Cet atelier de programmation vous explique comment ajouter une carte Google à une application Flutter. Les concepts et les blocs de code qui ne concernent pas cet aspect ne sont pas abordés, et vous sont fournis afin que vous puissiez simplement les copier et les coller.
Qu'attendez-vous de cet atelier de programmation ?
2. Configurer votre environnement Flutter
Pour cet atelier, vous avez besoin de deux logiciels : le SDK Flutter et un éditeur. Nous utilisons pour cet atelier Android Studio. Toutefois, vous pouvez vous servir de l'éditeur de votre choix.
Vous pouvez exécuter cet atelier de programmation sur l'un des appareils suivants :
- Un appareil physique (Android ou iOS) en mode développeur, branché à votre ordinateur
- Le simulateur iOS (nécessite d'installer les outils Xcode)
- L'émulateur Android (doit être configuré dans Android Studio)
3. Premiers pas
Premiers pas avec Flutter
Pour commencer à utiliser Flutter, il vous suffit d'utiliser l'outil de ligne de commande Flutter pour créer tout le code nécessaire à une expérience de démarrage simple.
$ flutter create google_maps_in_flutter Creating project google_maps_in_flutter... [Listing of created files elided] Wrote 127 files. All done! 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.
Ajouter le plug-in Flutter pour Google Maps en tant que dépendance
Pour ajouter des fonctionnalités à une application Flutter, il vous suffit d'utiliser les packages Pub. Dans cet atelier de programmation, vous devez installer le plugin Flutter pour Google Maps en exécutant la commande suivante depuis le répertoire du projet.
$ cd google_maps_in_flutter $ flutter pub add google_maps_flutter Resolving dependencies... async 2.6.1 (2.8.2 available) charcode 1.2.0 (1.3.1 available) + flutter_plugin_android_lifecycle 2.0.3 + google_maps_flutter 2.0.8 + google_maps_flutter_platform_interface 2.1.1 matcher 0.12.10 (0.12.11 available) meta 1.3.0 (1.7.0 available) + plugin_platform_interface 2.0.1 + stream_transform 2.0.0 test_api 0.3.0 (0.4.3 available) Downloading google_maps_flutter 2.0.8... Downloading flutter_plugin_android_lifecycle 2.0.3... Changed 5 dependencies!
Vous apprendrez également à utiliser Google Maps dans la version Web de Flutter. Cependant, la version Web du plug-in n'est pas encore fédérée. Vous devez donc l'ajouter à votre projet.
$ flutter pub add google_maps_flutter_web Resolving dependencies... async 2.6.1 (2.8.2 available) charcode 1.2.0 (1.3.1 available) + csslib 0.17.0 + flutter_web_plugins 0.0.0 from sdk flutter + google_maps 5.3.0 + google_maps_flutter_web 0.3.0+4 + html 0.15.0 + js 0.6.3 + js_wrapping 0.7.3 matcher 0.12.10 (0.12.11 available) meta 1.3.0 (1.7.0 available) + sanitize_html 2.0.0 test_api 0.3.0 (0.4.3 available) Changed 8 dependencies!
Configurer iOS platform
Pour obtenir la dernière version du SDK Google Maps sur iOS, vous devez utiliser la version minimale d'iOS 11. Modifiez le fichier ios/Podfile comme suit.
ios/Podfile
# Set platform to 11.0 to enable latest Google Maps SDK
platform :ios, '11.0' # Uncomment and set to 11.
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
Configurer Android minSDK
Pour utiliser le SDK Google Maps sur Android, vous devez définir minSDK
sur 20. Modifiez le fichier android/app/build.gradle comme suit.
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"
minSdkVersion 20 // Update from 16 to 20
targetSdkVersion 30
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
}
}
4. Ajouter Google Maps à l'application
L'importance des clés API
Pour utiliser Google Maps dans votre application Flutter, vous devez configurer un projet d'API avec Google Maps Platform, en suivant les instructions des pages Utiliser la clé API du SDK Maps pour Android, Utiliser la clé API du SDK Maps pour iOS et Utiliser la clé API du SDK Maps pour JavaScript. À l'aide des clés API, configurez les applications Android et iOS comme suit.
Ajouter une clé API pour une application Android
Pour ajouter une clé API à l'application Android, modifiez le fichier AndroidManifest.xml
dans android/app/src/main
. Ajoutez une seule entrée meta-data
contenant la clé API (créée à l'étape précédente) dans le nœud application
.
android/app/src/main/AndroidManifest.xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.google_maps_in_flutter">
<application
android:label="google_maps_in_flutter"
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:launchMode="singleTop"
android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize">
<meta-data
android:name="io.flutter.embedding.android.NormalTheme"
android:resource="@style/NormalTheme"
/>
<meta-data
android:name="io.flutter.embedding.android.SplashScreenDrawable"
android:resource="@drawable/launch_background"
/>
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<meta-data
android:name="flutterEmbedding"
android:value="2" />
</application>
</manifest>
Ajouter une clé API pour une application iOS
Pour ajouter une clé API à l'application iOS, modifiez le fichier AppDelegate.swift
dans ios/Runner
. Contrairement à Android, ajouter une clé API sur iOS nécessite de modifier le code source de l'application Runner. AppDelegate est le singleton principal qui fait partie du processus d'initialisation de l'application.
Vous devez apporter deux modifications à ce fichier. Tout d'abord, ajoutez une instruction #import
pour extraire les en-têtes Google Maps, puis appelez la méthode provideAPIKey()
du singleton GMSServices
. Cette clé API permet à Google Maps de diffuser correctement les tuiles de carte.
ios/Runner/AppDelegate.swift
import UIKit
import Flutter
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")
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
}
Ajouter une clé API pour une application Web
Pour ajouter une clé API à l'application Web, modifiez le fichier index.html
dans web
. Ajoutez une référence au script Maps JavaScript dans la section "head" avec votre clé API.
web/index.html
<head>
<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">
<!-- TODO: 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>
Ajouter une carte à l'écran
À présent, ajoutons une carte à l'écran. Modifiez lib/main.dart
comme suit :
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({Key? key}) : super(key: key);
@override
_MyAppState 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(
home: Scaffold(
appBar: AppBar(
title: const Text('Maps Sample App'),
backgroundColor: Colors.green[700],
),
body: GoogleMap(
onMapCreated: _onMapCreated,
initialCameraPosition: CameraPosition(
target: _center,
zoom: 11.0,
),
),
),
);
}
}
Exécuter l'application
Exécutez l'application Flutter sur iOS ou Android pour afficher une seule vue de la carte, centrée sur Portland. Vous pouvez aussi exécuter un émulateur Android ou un simulateur iOS. N'hésitez pas à modifier le centre de la carte pour afficher votre ville natale ou un autre endroit important pour vous.
$ flutter run
5. Positionner Google sur la carte
Google dispose de nombreux bureaux dans le monde entier : en Amérique du Nord, en Amérique latine, en Europe, en Asie-Pacifique, et en Afrique et au Moyen-Orient. Ces cartes présentent un avantage certain : elles disposent d'un point de terminaison d'API facile à utiliser afin de fournir des informations sur la position des bureaux au format JSON. Au cours de cette étape, vous placerez ces adresses sur la carte. Par ailleurs, vous utiliserez la génération de code pour analyser le contenu au format JSON.
Ajoutez trois dépendances Flutter au projet comme suit. Tout d'abord, ajoutez le package http
pour effectuer facilement des requêtes HTTP.
$ flutter pub add http Resolving dependencies... async 2.8.1 (2.8.2 available) + http 0.13.3 + http_parser 4.0.0 matcher 0.12.10 (0.12.11 available) + pedantic 1.11.1 test_api 0.4.2 (0.4.3 available) Changed 3 dependencies!
Ensuite, ajoutez json_serializable pour déclarer la structure d'objets représentant des documents JSON.
$ flutter pub add json_serializable Resolving dependencies... + _fe_analyzer_shared 25.0.0 + analyzer 2.2.0 + args 2.2.0 async 2.8.1 (2.8.2 available) + build 2.1.0 + build_config 1.0.0 + checked_yaml 2.0.1 + cli_util 0.3.3 + convert 3.0.1 + crypto 3.0.1 + dart_style 2.0.3 + file 6.1.2 + glob 2.0.1 + json_annotation 4.1.0 + json_serializable 5.0.0 + logging 1.0.1 matcher 0.12.10 (0.12.11 available) + package_config 2.0.0 + pub_semver 2.0.0 + pubspec_parse 1.0.0 + source_gen 1.1.0 + source_helper 1.2.1 test_api 0.4.2 (0.4.3 available) + watcher 1.0.0 + yaml 3.1.0 Downloading analyzer 2.2.0... Downloading _fe_analyzer_shared 25.0.0... Changed 22 dependencies!
Enfin, ajoutez build_runner comme dépendance au moment du développement. Elle sera utilisée pour générer du code ultérieurement.
$ flutter pub add --dev build_runner Resolving dependencies... async 2.8.1 (2.8.2 available) + build_daemon 3.0.0 + build_resolvers 2.0.4 + build_runner 2.1.1 + build_runner_core 7.1.0 + built_collection 5.1.0 + built_value 8.1.2 + code_builder 4.1.0 + fixnum 1.0.0 + frontend_server_client 2.1.2 + graphs 2.0.0 + http_multi_server 3.0.1 + io 1.0.3 + js 0.6.3 matcher 0.12.10 (0.12.11 available) + mime 1.0.0 + pool 1.5.0 + shelf 1.2.0 + shelf_web_socket 1.0.1 test_api 0.4.2 (0.4.3 available) + timing 1.0.0 + web_socket_channel 2.1.0 Changed 19 dependencies!
Analyser le contenu JSON avec la génération de code
Notez que les données JSON renvoyées par le point de terminaison de l'API présentent une structure régulière. Vous pouvez générer le code afin de rassembler ces données dans des objets que vous pouvez utiliser dans le code.
Dans le répertoire lib/src
, créez un fichier locations.dart
et décrivez la structure des données JSON renvoyées comme suit :
lib/src/locations.dart
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:json_annotation/json_annotation.dart';
import 'package:flutter/services.dart' show rootBundle;
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));
}
} catch (e) {
print(e);
}
// Fallback for when the above HTTP request fails.
return Locations.fromJson(
json.decode(
await rootBundle.loadString('assets/locations.json'),
),
);
}
Une fois que vous avez ajouté ce code, votre IDE (si vous en utilisez un) doit afficher des vaguelettes rouges, car il fait référence à un fichier correspondant inexistant (locations.g.dart.
). Le fichier généré est converti entre des structures JSON non saisies et des objets nommés. Créez-le en exécutant la commande build_runner
:
$ flutter pub 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)
Le code devrait à nouveau être analysé correctement. Vous devez ensuite ajouter le fichier locations.json utilisé dans la fonction getGoogleOffices
. En effet, les données statiques chargées dans cette fonction sont diffusées sans en-têtes CORS. Elles ne seront donc pas chargées dans un navigateur Web. Les applications Flutter sur Android et iOS n'ont pas besoin d'en-têtes CORS, mais l'accès aux données mobiles est souvent difficile.
Accédez à https://about.google/static/data/locations.json
dans votre navigateur, puis enregistrez le contenu dans le répertoire des éléments. Vous pouvez également utiliser la ligne de commande suivante :
$ 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
Une fois que vous avez téléchargé le fichier contenant les éléments, ajoutez-le à la section Flutter du fichier pubspec.yaml
.
pubspec.yaml
flutter:
uses-material-design: true
assets:
- assets/locations.json
Modifiez le fichier main.dart
pour demander les données cartographiques, puis utilisez les informations renvoyées pour ajouter les adresses à la carte :
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({Key? key}) : super(key: key);
@override
_MyAppState 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(
home: Scaffold(
appBar: AppBar(
title: const Text('Google Office Locations'),
backgroundColor: Colors.green[700],
),
body: GoogleMap(
onMapCreated: _onMapCreated,
initialCameraPosition: const CameraPosition(
target: LatLng(0, 0),
zoom: 2,
),
markers: _markers.values.toSet(),
),
),
);
}
}
Ce code effectue plusieurs opérations :
- Dans
_onMapCreated
, il utilise le code d'analyse JSON de l'étape précédente, et affiche l'étatawait
jusqu'à ce qu'elle soit chargée. Il utilise ensuite les données renvoyées pour créer desMarker
dans un rappelsetState()
. Une fois que l'application reçoit de nouveaux repères, setState indique à Flutter de repeindre l'écran pour afficher les adresses. - Les repères sont stockés dans un
Map
associé au widgetGoogleMap
. Les repères sont ainsi associés à la bonne carte. Vous pouvez tout à fait disposer de plusieurs cartes et afficher des repères différents dans chacune d'elles.
Voici une capture d'écran de ce que vous avez réussi à faire : À ce stade, vous pouvez y ajouter de nombreux éléments intéressants. Par exemple, vous pouvez ajouter une vue sous forme de liste qui permette de déplacer la carte et d'effectuer un zoom lorsque l'utilisateur clique sur une adresse. C'est à vous de tester !
6. Étapes suivantes
Félicitations !
Vous avez terminé l'atelier de programmation et créé une application Flutter qui inclut une carte Google. Vous avez également interagi avec un service Web JSON.
Pour aller plus loin
Cet atelier de programmation vous a appris à visualiser plusieurs points sur une carte. Beaucoup d'applications mobiles s'appuient sur cette fonctionnalité pour répondre à un grand nombre de besoins différents. D'autres ressources peuvent vous aider à aller plus loin :
- Développer des applications mobiles avec Flutter et Google Maps (conférence présentée lors de Cloud Next 2019)
- Le package
google_maps_webservice
d'Hadrien Lejard, contenant les services Web Google Maps (par exemple, l'API Directions, l'API Distance Matrix et l'API Places, très simples d'utilisation) - Si vous souhaitez examiner les différentes options permettant d'utiliser une API via REST JSON, consultez le post d'Andrew Brogdon sur Medium pour connaître les différentes possibilités d'utilisation des API REST JSON