הוספה של מפות Google לאפליקציית Flutter

1. מבוא

‫Flutter הוא SDK של Google לאפליקציות לנייד, שמאפשר ליצור חוויות מקוריות באיכות גבוהה ב-iOS וב-Android בזמן שיא.

בעזרת הפלאגין של מפות Google ל-Flutter, אתם יכולים להוסיף לאפליקציה שלכם מפות שמבוססות על נתונים של מפות Google. התוסף מטפל באופן אוטומטי בגישה לשרתי מפות Google, בהצגת המפה ובתגובה למחוות של המשתמשים, כמו קליקים וגרירות. אפשר גם להוסיף סמנים למפה. האובייקטים האלה מספקים מידע נוסף על מיקומים במפה ומאפשרים למשתמשים לבצע אינטראקציה עם המפה.

מה תפַתחו

ב-codelab הזה תבנו אפליקציה לנייד עם מפת Google באמצעות Flutter SDK. האפליקציה שלכם:

  • הצגת מפה של Google
  • אחזור נתוני מפה משירות אינטרנט
  • הצגת הנתונים האלה כסמנים במפה

צילום מסך של אפליקציית Flutter עם מפת Google שפועלת בסימולטור של אייפון, עם הדגשה של Mountain View

מה זה Flutter?

ל-Flutter יש שלוש יכולות ליבה.

  • פיתוח מהיר: אפשר ליצור אפליקציות ל-Android ול-iOS באלפיות השנייה באמצעות Stateful Hot Reload.
  • גמיש ובעל יכולות הבעה: אפשר להשיק תכונות במהירות, תוך התמקדות בחוויית משתמש נייטיבית.
  • ביצועים מקוריים ב-iOS וב-Android: הווידג'טים של Flutter משלבים את כל ההבדלים הקריטיים בין הפלטפורמות – כמו גלילה, ניווט, סמלים וגופנים – כדי לספק ביצועים מקוריים מלאים.

במפות Google יש:

  • כיסוי של 99% מהעולם: אפשר לבנות על בסיס נתונים מהימנים ומקיפים ביותר מ-200 מדינות וטריטוריות.
  • 25 מיליון עדכונים מדי יום: אתם יכולים לסמוך על מידע מדויק על מיקום בזמן אמת.
  • מיליארד משתמשים פעילים בחודש: אתם יכולים להרחיב את הפעילות שלכם בביטחון, בעזרת התשתית של מפות Google.

ב-Codelab הזה תלמדו איך ליצור חוויה של מפות Google באפליקציית Flutter ל-iOS ול-Android.

מה תלמדו

  • איך יוצרים אפליקציית Flutter חדשה.
  • איך מגדירים פלאגין של מפות Google ל-Flutter.
  • איך מוסיפים סמנים למפה באמצעות נתוני מיקום משירות אינטרנט.

ב-codelab הזה נתמקד בהוספת מפת Google לאפליקציית Flutter. מושגים ובלוקים של קוד שלא רלוונטיים מוצגים בקצרה, ואתם יכולים פשוט להעתיק ולהדביק אותם.

מה היית רוצה ללמוד בסדנת הקוד הזו?

אני חדש בנושא ואני רוצה לקבל סקירה כללית טובה. יש לי ידע בנושא הזה, אבל אני רוצה לרענן אותו. אני מחפש קוד לדוגמה לשימוש בפרויקט שלי. אני מחפש הסבר על משהו ספציפי.

2. הגדרת סביבת Flutter

כדי להשלים את שיעור ה-Lab הזה, תצטרכו שני סוגי תוכנה: Flutter SDK ועורך. ב-codelab הזה מניחים שמשתמשים ב-Android Studio, אבל אפשר להשתמש בכל כלי עריכה אחר.

אפשר להריץ את ה-codelab הזה באמצעות כל אחד מהמכשירים הבאים:

  • מכשיר פיזי (Android או iOS) שמחובר למחשב ומוגדר למצב פיתוח.
  • סימולטור iOS. (נדרשת התקנה של כלי Xcode).
  • אמולטור Android. (נדרשת הגדרה ב-Android Studio).

3. תחילת העבודה

איך מתחילים לעבוד עם Flutter

הדרך הקלה ביותר להתחיל להשתמש ב-Flutter היא להשתמש בכלי של שורת הפקודה של Flutter כדי ליצור את כל הקוד הנדרש לחוויית התחלה פשוטה.

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

הוספת הפלאגין של מפות Google ל-Flutter כתלות

קל להוסיף יכולות לאפליקציית Flutter באמצעות חבילות Pub. ב-Codelab הזה תציגו את התוסף Google Maps Flutter על ידי הפעלת הפקודה הבאה מספריית הפרויקט.

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

הגדרת iOS platform

כדי לקבל את הגרסה העדכנית ביותר של Google Maps SDK ב-iOS, נדרשת גרסה מינימלית של פלטפורמת iOS 14. משנים את החלק העליון של קובץ התצורה ios/Podfile באופן הבא.

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'

הגדרת Android minSDK

כדי להשתמש ב-SDK של מפות Google ב-Android, צריך להגדיר את minSdk ל-21. משנים את קובץ התצורה android/app/build.gradle באופן הבא.

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. הוספת מפות Google לאפליקציה

הכול קשור למפתחות ה-API

כדי להשתמש במפות Google באפליקציית Flutter, צריך להגדיר פרויקט API עם Google Maps Platform, לפי ההוראות במאמרים בנושא שימוש במפתח API ב-SDK של מפות ל-Android, שימוש במפתח API ב-SDK של מפות ל-iOS ושימוש במפתח API ב-Maps JavaScript API. אחרי שמקבלים את מפתחות ה-API, מבצעים את השלבים הבאים כדי להגדיר את האפליקציות ל-Android ול-iOS.

הוספת מפתח API לאפליקציית Android

כדי להוסיף מפתח API לאפליקציית Android, עורכים את הקובץ AndroidManifest.xml ב-android/app/src/main. מוסיפים רשומה אחת meta-data שמכילה את מפתח ה-API שנוצר בשלב הקודם בתוך הצומת 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>

הוספת מפתח API לאפליקציה ל-iOS

כדי להוסיף מפתח API לאפליקציית iOS, עורכים את הקובץ AppDelegate.swift ב-ios/Runner. בניגוד ל-Android, הוספת מפתח API ב-iOS מחייבת שינויים בקוד המקור של אפליקציית Runner. המחלקה AppDelegate היא סינגלטון הליבה שמהווה חלק מתהליך האתחול של האפליקציה.

תבצע שני שינויים בקובץ הזה. קודם מוסיפים הצהרה של #import כדי לאחזר את הכותרות של מפות Google, ואז קוראים לשיטה provideAPIKey() של ה-singleton GMSServices. מפתח ה-API הזה מאפשר למפות Google להציג קטעי מפה בצורה תקינה.

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

הוספת מפתח API לאפליקציית אינטרנט

כדי להוסיף מפתח API לאפליקציית האינטרנט, עורכים את הקובץ index.html ב-web. מוסיפים הפניה לסקריפט Maps JavaScript בקטע head, עם מפתח ה-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>

הצגת מפה על המסך

עכשיו אפשר להציג מפה על המסך. מחליפים את התוכן של lib/main.dart בתוכן הבא.

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

הפעלת האפליקציה

מריצים את אפליקציית Flutter ב-iOS או ב-Android כדי לראות תצוגת מפה אחת, עם פורטלנד במרכז. אפשר גם להפעיל אמולטור של Android או סימולטור של iOS. אתם יכולים לשנות את מרכז המפה כך שיציג את עיר הולדתכם או מקום אחר שחשוב לכם.

$ flutter run

צילום מסך של אפליקציית Flutter עם מפת Google שפועלת בסימולטור של אייפון

צילום מסך של אפליקציית Flutter עם מפת Google שפועלת באמולטור Android

5. הוספת עסק למפות Google

ל-Google יש הרבה משרדים ברחבי העולם, מצפון אמריקה, אמריקה הלטינית, אירופה, אסיה והאוקיינוס השקט ועד אפריקה והמזרח התיכון. היתרון במיפויים האלה הוא שאם בודקים אותם, מגלים שיש להם נקודת קצה ל-API שקל להשתמש בה כדי לספק את פרטי מיקום המשרד בפורמט JSON. בשלב הזה, ממקמים את מיקומי המשרדים האלה במפה. בשלב הזה, תשתמשו ביצירת קוד כדי לנתח JSON.

מוסיפים לפרויקט שלוש תלויות חדשות של Flutter באופן הבא. מוסיפים את החבילה http כדי ליצור בקשות HTTP בקלות, את החבילות json_serializable ו-json_annotation כדי להצהיר על מבנה האובייקט לייצוג מסמכי JSON, ואת החבילה build_runner לתמיכה ביצירת קוד.

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

ניתוח JSON באמצעות יצירת קוד

יכול להיות ששמתם לב שלנתוני ה-JSON שמוחזרים מנקודת קצה ל-API יש מבנה רגיל. יהיה שימושי ליצור את הקוד כדי להמיר את הנתונים לאובייקטים שאפשר להשתמש בהם בקוד.

בספרייה lib/src, יוצרים קובץ locations.dart ומתארים את המבנה של נתוני ה-JSON שמוחזרים באופן הבא:

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

אחרי שמוסיפים את הקוד הזה, סביבת הפיתוח המשולבת (אם משתמשים בה) אמורה להציג קווים אדומים מתחת לקוד, כי הוא מפנה לקובץ מקביל שלא קיים, locations.g.dart. הקובץ שנוצר ממיר בין מבני JSON לא מוקלדים לבין אובייקטים עם שמות. כדי ליצור אותו, מריצים את הפקודה build_runner באופן הבא:

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

הקוד אמור לעבור ניתוח ללא שגיאות. בשלב הבא, צריך להוסיף את קובץ הגיבוי locations.json שמשמש בפונקציה getGoogleOffices. אחת הסיבות לכלול את הגיבוי הזה היא שהנתונים הסטטיים שנטענים בפונקציה הזו מוגשים ללא כותרות CORS, ולכן הטעינה שלהם בדפדפן אינטרנט תיכשל. אפליקציות Flutter ל-Android ול-iOS לא צריכות כותרות CORS, אבל הגישה לנתונים בנייד יכולה להיות בעייתית גם במקרים הטובים ביותר.

בדפדפן, עוברים אל https://about.google/static/data/locations.json ושומרים את התוכן בספריית הנכסים. לחלופין, אפשר להשתמש בשורת הפקודה באופן הבא.

$ 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

אחרי שמורידים את קובץ הנכס, מוסיפים אותו לקטע flutter בקובץ pubspec.yaml.

pubspec.yaml

flutter:
  uses-material-design: true

  assets:
    - assets/locations.json

משנים את הקובץ main.dart כדי לבקש את נתוני המפה, ואז משתמשים במידע שמוחזר כדי להוסיף משרדים למפה:

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

הקוד הזה מבצע כמה פעולות:

  • ב-_onMapCreated, הקוד משתמש בקוד לניתוח JSON מהשלב הקודם, awaiting עד שהוא נטען. לאחר מכן המערכת משתמשת בנתונים שמוחזרים כדי ליצור Markers בתוך קריאה חוזרת (callback) של setState(). אחרי שהאפליקציה מקבלת סמנים חדשים, הפונקציה setState מסמנת ל-Flutter לצבוע מחדש את המסך, וכך מיקומי המשרדים מוצגים.
  • הסמנים מאוחסנים בMap שמשויך לווידג'ט GoogleMap. כך מקשרים את הסמנים למפה הנכונה. כמובן שאפשר להוסיף כמה מפות ולהציג בכל אחת מהן סמנים שונים.

צילום מסך של אפליקציית Flutter עם מפת Google שפועלת בסימולטור של אייפון, עם הדגשה של Mountain View

הנה צילום מסך של מה שהשגת. בשלב הזה אפשר להוסיף הרבה דברים מעניינים. לדוגמה, אפשר להוסיף תצוגת רשימה של המשרדים, שתזיז את המפה ותשנה את רמת הזום שלה כשהמשתמש ילחץ על משרד מסוים. אבל כמו שאומרים, התרגיל הזה נשאר לקורא!

6. השלבים הבאים

מעולה!

סיימתם את ה-codelab ויצרתם אפליקציית Flutter עם מפת Google! הייתה לכם גם אינטראקציה עם שירות אינטרנט בפורמט JSON.

השלבים הבאים האחרים

ב-codelab הזה יצרנו חוויה להצגת מספר נקודות במפה. יש מספר אפליקציות לנייד שמבוססות על היכולת הזו כדי לתת מענה לצרכים שונים של משתמשים. יש עוד מקורות מידע שיכולים לעזור לכם להרחיב את הידע בנושא: