إضافة "خرائط Google" إلى تطبيق Flutter

1. مقدمة

Flutter هي حزمة تطوير برامج (SDK) للتطبيقات للأجهزة الجوّالة من Google لتقديم تجارب عالية الجودة أصلية على أجهزة iOS وAndroid في وقت قياسي.

باستخدام مكوّن Flutter في "خرائط Google"، يمكنك إضافة خرائط إلى تطبيقك استنادًا إلى بيانات "خرائط Google". يتعامل المكوّن الإضافي تلقائيًا مع الدخول إلى خوادم خرائط Google وعرض الخريطة والاستجابة لإيماءات المستخدم مثل النقرات والسحب. يمكنك أيضًا إضافة محددات إلى خريطتك. توفر هذه الكائنات معلومات إضافية لمواقع الخريطة، وتسمح للمستخدم بالتفاعل مع الخريطة.

ما الذي ستنشئه

في هذا الدرس التطبيقي، ستُنشئ تطبيقًا للأجهزة الجوّالة يعرض "خرائط Google" باستخدام حزمة Flutter SDK. سينفّذ تطبيقك ما يلي:

  • عرض خريطة Google
  • استرداد بيانات الخريطة من خدمة ويب
  • عرض هذه البيانات كعلامات على الخريطة

لقطة شاشة لتطبيق Flutter مع خريطة من "خرائط Google" يتم تشغيلها في جهاز محاكاة لأجهزة iPhone مع تمييز "ماونتن فيو"

ما هو Flutter؟

يوفّر Flutter ثلاث إمكانات أساسية.

  • سريعة التطوير: يمكنك إنشاء تطبيقات Android وiOS في أجزاء من الثانية باستخدام Stateful Hot Reload.
  • التعبير والمرونة: يمكنك شحن الميزات بسرعة مع التركيز على تجارب المستخدمين النهائيين الأصلية.
  • الأداء الأصلي على كل من iOS وAndroid: تدمج تطبيقات Flutter التطبيقات المصغّرة جميع الاختلافات المهمة في الأنظمة الأساسية، مثل التمرير والتنقّل والرموز والخطوط، لتقديم أداء كامل أصلي.

تحتوي خرائط Google على:

  • تغطية بنسبة% 99 حول العالم: يمكنك إنشاء المحتوى باستخدام بيانات موثوقة وشاملة لأكثر من 200 بلد ومنطقة.
  • 25 مليون تعديل يوميًا: يمكنك الاعتماد على معلومات دقيقة عن الموقع الجغرافي في الوقت الفعلي.
  • مليار مستخدم نشط شهريًا: يمكنك التوسُّع بثقة بفضل "خرائط Google" البنية الأساسية.

يرشدك هذا الدرس التطبيقي إلى خطوات إنشاء تجربة في "خرائط Google" في تطبيق Flutter لكل من نظامَي التشغيل iOS وAndroid.

المعلومات التي ستطّلع عليها

  • كيفية إنشاء تطبيق Flutter جديد
  • كيفية إعداد مكوّن Flutter الإضافي في "خرائط Google"
  • كيفية إضافة محدّدات موقع إلى خريطة باستخدام بيانات الموقع من خدمة ويب.

يركّز هذا الدرس التطبيقي حول الترميز على إضافة إحدى "خرائط Google" إلى تطبيق Flutter. يتم مسح المفاهيم غير ذات الصلة وكتل الرموز ويتم توفيرها لك لنسخها ولصقها ببساطة.

ما الذي تريد تعلّمه من هذا الدرس التطبيقي حول الترميز؟

أنا جديد في هذا الموضوع، وأريد نظرة عامة جيدة. أعرف معلومات عن هذا الموضوع، ولكن أريد تنشيطًا للذاكرة. أبحث عن رمز برمجي لاستخدامه في مشروعي. أريد تفسيرًا لمعلومة محدّدة.

2. إعداد بيئة Flutter

تحتاج إلى برنامجَين لإكمال هذا التمرين، وهما Flutter SDK ومحرِّر. يفترض هذا الدرس التطبيقي حول الترميز استخدام "استوديو Android"، ولكن يمكنك استخدام المحرِّر المفضّل لديك.

يمكنك تشغيل هذا الدرس التطبيقي حول الترميز باستخدام أي من الأجهزة التالية:

  • جهاز فعلي (Android أو iOS) متصل بجهاز الكمبيوتر وتم ضبطه على وضع مطور البرامج.
  • مُحاكي iOS. (يتطلب ذلك تثبيت أدوات Xcode.)
  • محاكي Android (يتطلّب ذلك إجراء عملية الإعداد في استوديو Android.)

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.

إضافة المكوّن الإضافي لبرنامج Flutter في "خرائط Google" كعنصر تابع

من السهل إضافة المزيد من الميزات إلى تطبيق Flutter باستخدام حِزم Pub. في هذا الدرس التطبيقي حول الترميز، تقدّم المكوّن الإضافي Flutter لخرائط Google من خلال تنفيذ الأمر التالي من دليل المشروع.

$ 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

للحصول على أحدث إصدار من حزمة تطوير البرامج (SDK) لـ "خرائط Google" على نظام التشغيل 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" إلى التطبيق

الأمر كله يتعلق بمفاتيح واجهة برمجة التطبيقات

لاستخدام "خرائط Google" في تطبيق Flutter، عليك إعداد مشروع واجهة برمجة التطبيقات باستخدام منصة خرائط Google، باتّباع حزمة تطوير البرامج (SDK) للخرائط باستخدام مفتاح واجهة برمجة التطبيقات في Android وحزمة تطوير البرامج (SDK) لتطبيق "خرائط Google" لنظام التشغيل iOS. استخدام مفتاح واجهة برمجة التطبيقات واستخدام مفتاح واجهة برمجة التطبيقات لخرائط JavaScript. باستخدام مفاتيح واجهة برمجة التطبيقات في متناول يدك، يمكنك تنفيذ الخطوات التالية لضبط تطبيقات Android وiOS.

إضافة مفتاح واجهة برمجة تطبيقات لتطبيق Android

لإضافة مفتاح واجهة برمجة تطبيقات إلى تطبيق Android، عدِّل ملف AndroidManifest.xml في android/app/src/main. أضِف إدخال meta-data واحدًا يحتوي على مفتاح واجهة برمجة التطبيقات الذي تم إنشاؤه في الخطوة السابقة داخل عقدة 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>

إضافة مفتاح واجهة برمجة تطبيقات لتطبيق iOS

لإضافة مفتاح واجهة برمجة تطبيقات إلى تطبيق iOS، عدِّل ملف AppDelegate.swift في ios/Runner. على عكس نظام التشغيل Android، تتطلب إضافة مفتاح واجهة برمجة تطبيقات على نظام التشغيل iOS إجراء تغييرات على رمز المصدر في تطبيق Runner. أداة Appتفويض هو المستخدم الفردي الأساسي الذي يُعد جزءًا من عملية إعداد التطبيق.

أجرِ تغييرين على هذا الملف. أولاً، أضِف عبارة #import لسحب العناوين في "خرائط Google"، ثم استدعِ طريقة provideAPIKey() للفرد GMSServices المفرد. يعمل مفتاح واجهة برمجة التطبيقات هذا على تفعيل "خرائط 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)
  }
}

إضافة مفتاح واجهة برمجة تطبيقات لتطبيق ويب

لإضافة مفتاح واجهة برمجة تطبيقات إلى تطبيق الويب، عدِّل ملف index.html في web. أضف إشارة إلى نص JavaScript البرمجي للخرائط في قسم الرأس، باستخدام مفتاح واجهة برمجة التطبيقات.

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 مع &quot;خرائط Google&quot; أثناء تشغيله في جهاز محاكاة لأجهزة iPhone

لقطة شاشة لتطبيق Flutter مع &quot;خرائط Google&quot; ويتم تشغيله في محاكي Android.

5- وضع Google على الخريطة

تضم Google العديد من المكاتب في جميع أنحاء العالم، بدءًا من أمريكا الشمالية وأمريكا اللاتينية وأوروبا وآسيا والمحيط الهادئ إلى أفريقيا الشرق الأوسط إن الشيء الرائع في هذه الخرائط، إذا تحققت منها، هو أنها تحتوي على نقطة نهاية لواجهة برمجة التطبيقات يسهل استخدامها لتوفير معلومات موقع المكتب بتنسيق 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 التي يتم عرضها من نقطة نهاية واجهة برمجة التطبيقات لها بنية عادية. سيكون من السهل إنشاء التعليمات البرمجية لتنظيم تلك البيانات إلى كائنات يمكنك استخدامها في التعليمات البرمجية.

في دليل 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>,
  );
}

بعد إضافة هذا الرمز، من المفترض أن يعرض IDE (إذا كنت تستخدم واحدًا) بعض التعريض المضغوط باللون الأحمر، لأنّه يشير إلى ملف شقيق غير موجود. 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)

يجب أن يتم تحليل التعليمة البرمجية الآن بشكل نظيف مرة أخرى. بعد ذلك، يجب أن نضيف ملف location.json الاحتياطي المستخدَم في الدالة getGoogleOffices. أحد أسباب تضمين هذا الإجراء الاحتياطي هو أن البيانات الثابتة التي يتم تحميلها في هذه الدالة يتم تقديمها بدون عناوين CORS، وبالتالي لن يتم تحميلها في متصفح الويب. لا يحتاج تطبيقا Android وiOS Flutter إلى عناوين سياسة مشاركة الموارد المتعددة المصادر (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 من الخطوة السابقة await إلى أن يتم تحميله. وهي تستخدم بعد ذلك البيانات التي تم إرجاعها لإنشاء Marker داخل استدعاء setState(). بعد أن يتلقّى التطبيق علامات جديدة، يحدِّد setState برنامج Flutter لإعادة طلاء الشاشة، ما يؤدي إلى عرض المواقع الجغرافية للمكاتب.
  • يتم تخزين العلامات في Map المرتبط بالأداة GoogleMap. يؤدي هذا إلى ربط العلامات بالخريطة الصحيحة. يمكنك، بالطبع، الحصول على خرائط متعددة وعرض علامات مختلفة في كل منها.

لقطة شاشة لتطبيق Flutter مع خريطة من &quot;خرائط Google&quot; يتم تشغيلها في جهاز محاكاة لأجهزة iPhone مع تمييز &quot;ماونتن فيو&quot;

إليك لقطة شاشة لما أنجزته. هناك العديد من الإضافات المثيرة للاهتمام التي يمكن إجراؤها في هذه المرحلة. على سبيل المثال، يمكنك إضافة عرض قائمة للمكاتب التي تعمل على تحريك الخريطة وتكبيرها عندما ينقر المستخدم على مكتب ما، ولكن كما يقول، يتم ترك هذا التمرين للقارئ!

6- الخطوات التالية

تهانينا

لقد أكملت التمرين التطبيقي حول الترميز وأنشأت تطبيق Flutter باستخدام "خرائط Google". وقد تفاعلت أيضًا مع خدمة JSON على الويب.

الخطوات التالية الأخرى

صمّم هذا الدرس التطبيقي حول الترميز تجربة لعرض عدد من النقاط على الخريطة. وهناك عدد من التطبيقات للأجهزة الجوّالة التي تعتمد على هذه الإمكانية لتلبية الكثير من احتياجات المستخدمين المختلفة. هناك مراجع أخرى يمكن أن تساعدك في تحقيق ذلك: