FlutterでGoogle Map上にピンを刺してみる

2021-05-22

はじめに

運び屋(字幕版) を見ました。

80代のおじいちゃんが孫の婚約パーティに出向くも、妻にも娘に「お前何できてるんや?」って態度で追い返されてしまいます。消沈の中、パーティの出席者からうまい話があるよと荷物運びの依頼を受けることになります。

何度か運送を繰り返している中、積み荷が麻薬だと知るも金の為に罪悪感もありつつ繰り返してしまいます。

組織の中に警察に雇われ情報をリークしている捜査も伸びてきてどうなるおじいちゃん!! という話でした。

マーカを表示する方法

Google Map上にピンを刺すコードを書いていきましょう。

Google Maps for Flutterとして公開されているパッケージを使用しています。 google_maps_flutter

この記事のコードをベースに追加していきます。 FlutterでGoogle Mapを呼び出すアプリを作ってみよう

Markerを表示する

まずは、マーカーを指定した座標に表示するコードを追加します。 main.dartにMarkerオブジェクトを返す関数を追加し、マーカーを刺す位置とIDを設定していきます。

static final LatLng _kMapCenter1 =
    LatLng(37.43296265331129, -122.08832357078792);

Set<Marker> _createMarker() {
  return {
    Marker(
      markerId: MarkerId("marker_1"),
      position: _kMapCenter1,
    ),
  };
}

この関数をGoogleMapクラスを生成し、その中で先程作った関数を呼び出します。

Widget build(BuildContext context) {
  return new Scaffold(
    body: GoogleMap(
      mapType: MapType.hybrid,
      markers: _createMarker(),
      initialCameraPosition: _kGooglePlex,
      onMapCreated: (GoogleMapController controller) {
        _controller.complete(controller);
      },
    ),

マーカを表示するコードの基本形はこれで完成です。

実行結果

上手く動作すると指定した座標にマーカが表示されます。

複数のマーカを表示する

先程のコード上に追加して複数マーカーを表示してみます。 やり方は簡単ですが、Markerを複数返すだけです。

例えば、事前に複数の店舗情報をSQLで用意しておき、取得したデータ分だけマーカを追加するように作り込んでいくと中々見れたものになりそうですね。

static final LatLng _kMapCenter1 =
    LatLng(37.43296265331129, -122.08832357078792);

+static final LatLng _kMapCenter2 =
+    LatLng(37.43306265331129, -122.08830057078792);

Set<Marker> _createMarker() {
  return {
    Marker(
      markerId: MarkerId("marker_1"),
      position: _kMapCenter1,
    ),
+    Marker(
+      markerId: MarkerId("marker_2"),
+      position: _kMapCenter2,
+    ),
  };
}

実行結果

マーカーの位置が近ずぎて表示すると1つのマーカみたいに表示されますが… 右下の+ーアイコンで表示距離を変更するとこのように複数マーカ表示されていることが確認できます。

マーカに情報を追加する

infoWindowをMarker()の中に追加することで実装できます。 店の名前をここに入れていくのが良いんでないでしょうか。

Set<Marker> _createMarker() {
  return {
    Marker(
        markerId: MarkerId("marker_1"),
        position: _kMapCenter1,
        infoWindow: InfoWindow(title: "腐ったみかん")),
    Marker(
      markerId: MarkerId("marker_2"),
      position: _kMapCenter2,
    ),
  };
}

実行結果

マーカに情報を追加する

infoWindow: InfoWindow(title: "腐ったみかん", snippet: '俺は腐ったみかんじゃない')),

infoWindowのsnippetを追加するとこんなふうに表示されます。

今回実装したコードの全文

こんな感じになります。

import 'dart:async';
import 'dart:typed_data';
import 'dart:ui' as ui;
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Google Maps Demo',
      home: MapSample(),
    );
  }
}

class MapSample extends StatefulWidget {
  @override
  State<MapSample> createState() => MapSampleState();
}

class MapSampleState extends State<MapSample> {
  Completer<GoogleMapController> _controller = Completer();

  static final CameraPosition _kGooglePlex = CameraPosition(
    target: LatLng(37.43296265331129, -122.08832357078792),
    zoom: 13.0000,
    bearing: 0.0,
    tilt: 0.0,
  );

  static final CameraPosition _kLake = CameraPosition(
      bearing: 192.8334901395799,
      target: LatLng(37.43296265331129, -122.08832357078792),
      tilt: 59.440717697143555,
      zoom: 10.151926040649414);

  static final LatLng _kMapCenter1 =
      LatLng(37.43296265331129, -122.08832357078792);

  static final LatLng _kMapCenter2 =
      LatLng(37.43306265331129, -122.08830057078792);

  Set<Marker> _createMarker() {
    return {
      Marker(
          markerId: MarkerId("marker_1"),
          position: _kMapCenter1,
          icon: BitmapDescriptor.defaultMarkerWithHue(5.4),
          infoWindow: InfoWindow(title: "腐ったみかん", snippet: '俺は腐ったみかんじゃない')),
      Marker(
        markerId: MarkerId("marker_2"),
        position: _kMapCenter2,
      ),
    };
  }

  Future<BitmapDescriptor> getMarker() async {
    final ui.PictureRecorder pictureRecorder = ui.PictureRecorder();

    final Canvas canvas = Canvas(pictureRecorder);
    final Paint paint = Paint();
    paint.color = Colors.red;
    canvas.drawCircle(Offset(100, 35), 25, paint);

    final image = await pictureRecorder.endRecording().toImage(100, 100);
    final data = await image.toByteData(format: ui.ImageByteFormat.webp);
    final Uint8List bytes = data!.buffer.asUint8List();
    return BitmapDescriptor.fromBytes(bytes);
  }

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      body: GoogleMap(
        mapType: MapType.hybrid,
        markers: _createMarker(),
        initialCameraPosition: _kGooglePlex,
        onMapCreated: (GoogleMapController controller) {
          _controller.complete(controller);
        },
      ),
      floatingActionButton: FloatingActionButton.extended(
        onPressed: _goToTheLake,
        label: Text('To the lake!'),
        icon: Icon(Icons.directions_boat),
      ),
    );
  }

  Future<void> _goToTheLake() async {
    final GoogleMapController controller = await _controller.future;
    controller.animateCamera(CameraUpdate.newCameraPosition(_kLake));
  }
}

まとめ

FlutterでGoogle Mapにマーカを追加する方法を理解しました。Android JavaでGoogle Mapを実装した時に比べると随分簡単で良いですね。

Flutter全然分かってないので、記事のネタとしては弱いですが少しずつできること増やしていきましょ。

FlutterではCanvasを画像データに変換してGoogle Map上に書き出すやり方もあるので次回はそれを試して行きたいですね。