Menambahkan Google Maps ke aplikasi Flutter

1. Pengantar

Flutter adalah SDK aplikasi seluler Google untuk membuat pengalaman native berkualitas tinggi di iOS dan Android dalam waktu singkat.

Dengan plugin Google Maps Flutter, Anda dapat menambahkan peta ke aplikasi berdasarkan data peta Google. Plugin ini akan otomatis menangani akses ke server Google Maps, tampilan peta, dan respons terhadap gestur pengguna seperti klik dan tarik. Anda juga dapat menambahkan penanda ke peta. Objek ini memberikan informasi tambahan untuk lokasi peta, dan memungkinkan pengguna berinteraksi dengan peta.

Yang akan Anda buat

Dalam codelab ini, Anda akan mem-build aplikasi seluler yang dilengkapi Peta Google menggunakan Flutter SDK. Aplikasi Anda akan:

  • Menampilkan Peta Google
  • Mengambil data peta dari layanan web
  • Menampilkan data ini sebagai penanda di Peta

Apa itu Flutter?

Flutter memiliki tiga kemampuan inti.

  • Dapat dikembangkan dengan cepat: Mem-build aplikasi Android dan iOS dalam hitungan milidetik dengan Stateful Hot Reload.
  • Ekspresif dan fleksibel: Dengan cepat mengirim fitur yang berfokus pada pengalaman native pengguna akhir.
  • Performa native di iOS dan Android: Widget Flutter mempertimbangkan semua perbedaan platform yang penting — seperti scroll, navigasi, ikon, dan font — untuk memberikan performa native yang lengkap.

Google Maps memiliki:

  • Cakupan dunia seluas 99%: Melakukan build dengan data yang andal dan komprehensif untuk lebih dari 200 negara dan wilayah.
  • 25 juta update setiap hari: Mengandalkan informasi lokasi real time yang akurat.
  • 1 miliar pengguna aktif bulanan: Melakukan penskalaan dengan pasti, yang didukung oleh infrastruktur Google Maps.

Codelab ini memandu Anda dalam membuat pengalaman Google Maps di aplikasi Flutter untuk iOS dan Android.

Yang akan Anda pelajari

  • Cara membuat aplikasi Flutter baru.
  • Cara mengonfigurasi plugin Google Maps Flutter.
  • Cara menambahkan Penanda ke peta, menggunakan data lokasi dari layanan web.

Codelab ini berfokus untuk menambahkan peta Google ke aplikasi Flutter. Konsep dan blok kode yang tidak relevan akan dibahas sekilas dan disediakan bagi Anda untuk disalin dan ditempelkan dengan mudah.

Apa yang ingin Anda pelajari dari codelab ini?

Saya baru mengenal topik ini, jadi saya ingin mendapatkan ringkasan yang bermanfaat. Saya sedikit paham soal topik ini, tetapi saya perlu mengingat kembali. Saya sedang mencari kode contoh untuk digunakan dalam project saya. Saya sedang mencari penjelasan tentang hal spesifik.

2. Menyiapkan lingkungan Flutter Anda

Anda memerlukan dua software untuk menyelesaikan lab ini: Flutter SDK dan editor. Codelab ini mengasumsikan bahwa pembaca akan menggunakan Android Studio, tetapi Anda dapat menggunakan editor pilihan sendiri.

Anda dapat menjalankan codelab ini menggunakan perangkat berikut:

  • Perangkat fisik (Android atau iOS) yang terhubung ke komputer Anda dan ditetapkan ke mode developer.
  • Simulator iOS: (Memerlukan penginstalan alat Xcode.)
  • Android Emulator. (Memerlukan penyiapan di Android Studio.)

3. Memulai

Mulai menggunakan Flutter

Cara termudah untuk mulai menggunakan Flutter adalah dengan memanfaatkan alat command line flutter untuk membuat semua kode yang diperlukan untuk pengalaman penggunaan awal yang sederhana.

$ 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.

Menambahkan plugin Google Maps Flutter sebagai dependensi

Menambahkan kemampuan tambahan ke aplikasi Flutter mudah dilakukan menggunakan paket Pub. Dalam codelab ini, Anda akan menerapkan plugin Google Maps Flutter dengan menjalankan perintah berikut dari direktori project.

$ 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!

Codelab ini juga akan membahas cara menggunakan Google Maps di Flutter untuk Web. Namun, versi Web plugin ini belum difederasi, jadi Anda juga perlu menambahkannya ke project.

$ 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!

Mengonfigurasi platform iOS

Untuk mendapatkan versi terbaru Google Maps SDK di iOS, Anda memerlukan versi minimum platform iOS 11. Ubah ios/Podfile sebagai berikut.

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'

Mengonfigurasi minSDK Android

Untuk menggunakan Google Maps SDK di Android, tetapkan minSDK ke 20. Ubah android/app/build.gradle sebagai berikut.

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. Menambahkan Google Maps ke aplikasi

Tentang kunci API

Untuk menjalankan Google Maps di aplikasi Flutter, Anda perlu mengonfigurasi project API menggunakan Google Maps Platform, dengan mengikuti petunjuk dalam artikel Menggunakan kunci API (Maps SDK for Android), Menggunakan kunci API (Maps SDK untuk iOS), dan Menggunakan kunci API (Maps JavaScript API). Setelah memiliki kunci API, lakukan langkah-langkah berikut untuk mengonfigurasi aplikasi Android dan iOS.

Menambahkan kunci API untuk aplikasi Android

Untuk menambahkan kunci API ke aplikasi Android, edit file AndroidManifest.xml di android/app/src/main. Tambahkan satu entri meta-data yang berisi kunci API yang dibuat pada langkah sebelumnya di dalam node 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>

Menambahkan kunci API untuk aplikasi iOS

Untuk menambahkan kunci API ke aplikasi iOS, edit file AppDelegate.swift di ios/Runner. Tidak seperti Android, untuk menambahkan kunci API di iOS, Anda harus mengubah kode sumber aplikasi Runner. AppDelegate adalah singleton inti yang merupakan bagian dari proses inisialisasi aplikasi.

Buat dua perubahan pada file ini. Pertama, tambahkan pernyataan #import untuk menarik header Google Maps, lalu panggil metode provideAPIKey() untuk singleton GMSServices. Dengan kunci API ini, Google Maps dapat menampilkan ubin peta dengan benar.

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)
  }
}

Menambahkan kunci API untuk aplikasi Web

Untuk menambahkan kunci API ke aplikasi Web, edit file index.html di web. Tambahkan referensi ke skrip Maps JavaScript di bagian head, dengan kunci API Anda.

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>

Menampilkan peta di layar

Sekarang saatnya menampilkan peta di layar. Update lib/main.dart sebagai berikut:

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,
          ),
        ),
      ),
    );
  }
}

Menjalankan aplikasi

Jalankan aplikasi Flutter di iOS atau Android untuk melihat satu tampilan peta, yang berpusat di Portland. Atau, jalankan Android Emulator atau simulator iOS. Jika ingin, ubah pusat peta untuk menampilkan kota asal Anda, atau tempat mana pun yang penting bagi Anda.

$ flutter run

5. Menempatkan Google di Peta

Google memiliki banyak kantor di seluruh dunia, mulai dari Amerika Utara, Amerika Latin, Eropa, Asia Pasifik, hingga Afrika & Timur Tengah. Jika Anda mempelajarinya, peta ini memiliki fungsi yang praktis, yaitu endpoint API yang mudah digunakan untuk menyediakan informasi lokasi kantor dalam format JSON. Pada langkah ini, Anda akan menempatkan lokasi kantor tersebut di peta. Pada langkah ini, Anda akan menggunakan pembuatan kode untuk menguraikan JSON.

Tambahkan tiga dependensi Flutter baru ke project sebagai berikut. Pertama, tambahkan paket http untuk membuat permintaan HTTP dengan mudah.

$ 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!

Selanjutnya, tambahkan json_serializable untuk mendeklarasikan struktur objek agar merepresentasikan dokumen 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!

Terakhir, tambahkan build_runner sebagai dependensi waktu pengembangan. Hal ini akan digunakan untuk pembuatan kode nanti di langkah ini.

$ 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!

Menguraikan JSON dengan pembuatan kode

Anda mungkin mendapati bahwa data JSON yang ditampilkan dari endpoint API memiliki struktur biasa. Akan lebih praktis jika Anda membuat kode untuk mengarahkan data tersebut ke objek yang dapat digunakan dalam kode.

Dalam direktori lib/src, buat file locations.dart dan deskripsikan struktur data JSON yang ditampilkan sebagai berikut:

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'),
    ),
  );
}

Setelah menambahkan kode ini, IDE (jika Anda menggunakannya) akan menampilkan beberapa garis bergelombang merah, karena IDE ini merujuk ke file seinduk yang tidak ada, locations.g.dart. File yang dihasilkan ini dikonversi antara struktur JSON tidak bertipe dan objek bernama. Buat file tersebut dengan menjalankan 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)

Sekarang, kode Anda seharusnya dianalisis kembali dengan rapi. Selanjutnya, kita harus menambahkan file fallback locations.json yang digunakan dalam fungsi getGoogleOffices. Salah satu alasan menyertakan fallback ini adalah karena data statis yang dimuat dalam fungsi ini disajikan tanpa header CORS, sehingga akan gagal dimuat di browser web. Aplikasi Flutter versi Android dan iOS tidak memerlukan header CORS, tetapi akses data seluler dapat bermasalah bahkan dalam kondisi yang menguntungkan.

Buka https://about.google/static/data/locations.json di browser Anda, lalu simpan kontennya ke dalam direktori aset. Atau, Anda dapat menggunakan command line sebagai berikut.

$ 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

Setelah file aset didownload, tambahkan file tersebut ke bagian flutter di file pubspec.yaml Anda.

pubspec.yaml

flutter:
  uses-material-design: true

  assets:
    - assets/locations.json

Ubah file main.dart untuk meminta data peta, lalu gunakan info yang ditampilkan untuk menambahkan kantor ke peta:

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(),
        ),
      ),
    );
  }
}

Kode ini melakukan beberapa operasi:

  • Di _onMapCreated, kode ini menggunakan kode penguraian JSON dari langkah sebelumnya. Status await akan ditampilkan sampai kode ini dimuat. Kemudian, kode ini akan menggunakan data yang ditampilkan untuk membuat Marker di dalam callback setState(). Setelah aplikasi menerima penanda baru, setState akan memberi tahu Flutter untuk melakukan repaint layar, yang menyebabkan lokasi kantor ditampilkan.
  • Penanda disimpan dalam Map yang terkait dengan widget GoogleMap. Tindakan ini akan menautkan penanda ke peta yang benar. Tentu saja, Anda dapat memiliki beberapa peta dan menampilkan penanda yang berbeda-beda di setiap peta.

71c460c73b1e061e.png

Berikut screenshot yang menampilkan hal yang telah Anda capai. Ada banyak tambahan menarik yang dapat dibuat pada tahap ini. Misalnya, Anda dapat menambahkan tampilan daftar kantor yang menggerakkan dan melakukan zoom pada peta saat pengguna mengklik sebuah kantor, tetapi hal ini bersifat opsional bagi pembaca.

6. Langkah berikutnya

Selamat.

Anda telah menyelesaikan codelab dan mem-build aplikasi Flutter dengan Google Map. Anda juga telah berinteraksi dengan Layanan Web JSON.

Langkah berikutnya yang lain

Codelab ini telah menciptakan pengalaman untuk memvisualisasikan sejumlah titik pada peta. Ada sejumlah aplikasi seluler yang memanfaatkan kemampuan ini untuk memenuhi berbagai kebutuhan pengguna. Berikut referensi lainnya dapat membantu Anda lebih lanjut: