google_maps_flutterパッケージのGoogle Map ClassやGoogleMapController Classのまとめ

# はじめに
この記事は [Flutter #1 AdventCalendar2020](https://qiita.com/advent-calendar/2020/flutter) の18日目の記事です。

普段はフリーランスでFlutter案件のお仕事を中心に行っています。
今回はお仕事でGoogle Mapを使っていろいろな機能を作った時に「今まで、何となくGoogle Mapを実装してたけど、ちゃんと勉強してみよう！」と思いよく使うプロパティやメソッドなどなどをまとめました。

## Google Map Class
- **onCameraMoveStarted**　
マップをドラッグして移動した時に呼び出される。

- **onCameraMove** 
onCameraMoveStarted開始後に常に地図の中心の緯度と経度を取得できる。
ドラッグやピンチなどのジェスチャ操作でも発生します。
状態変化があるたびに呼ばれます。printなど入れているとすごい数のprintが吐き出されるので注意です。
コールバックはCameraPositionが返ります。

```dart

CameraPosition(bearing: 0.0, target: LatLng(latitude, longitude), tilt: 0.0, zoom: 17.0)
```

- **onCameraIdle**　
アニメーションがキャンセルされた時やマップをドラッグして移動などマップの変化が起こるときには、最後にonCameraIdleが必ず１回呼ばれます。
コールバックはなし。

- **onLongPress**　
マップを長押しした場所の緯度と経度が返される。


- **onTap**　
マップをタップした場所の緯度と経度が返される。

```dart

GoogleMap(
  initialCameraPosition: CameraPosition(
    target: LatLng(latitude, longitude),
    zoom: 17,
  ),
  myLocationEnabled: true,
  markers: _markers,
  myLocationButtonEnabled: false,
  onMapCreated: (GoogleMapController googleMap) {
    _googleMapController.complete(googleMap);
  },
  onTap: (LatLng latLang) {
    print('Clicked: $latLang');
  },
  onLongPress: (LatLng latLang) {
    print('Tapped: $latLang');
  },
  onCameraIdle: () {
    print('CameraIdle');
  },
  onCameraMoveStarted: () {
    print('Move Started');
  },
  onCameraMove: (CameraPosition position) {
    print('Camera Move: $position');
  },
);
```


- **circles**

Set&lt;Circle&gt;
マップ上に円を表示。
地図上に範囲を示すのに使用します。
「指定エリアから〇〇km圏内」などの機能で使用される範囲です。

```dart
final circles = <Circle>{
  Circle(
    circleId: CircleId(id),
    center: LatLng(latitude, longitude),
    strokeWidth: 1,
    strokeColor: Colors.lightBlue,
    fillColor: Colors.lightBlue.withOpacity(0.1),
    radius: 100,
  )
};
GoogleMap(
  initialCameraPosition: CameraPosition(
    target: LatLng(latitude, longitude),
    zoom: 17,
  ),
  myLocationEnabled: true,
  markers: _markers,
  myLocationButtonEnabled: false,
  onMapCreated: (GoogleMapController googleMap) {
    _googleMapController.complete(googleMap);
  },
  circles: circles,
);
```

- **polygons**

Set&lt;Polygon&gt;
マップ上に指定した形のポリゴンを表示します。
ポリゴンの形は緯度と経度で指定して作ります。
こちらもcirclesと同様、地図上に範囲を示すのに使用します。

```dart
GoogleMap(
  initialCameraPosition: CameraPosition(
    target: LatLng(latitude, longitude),
    zoom: 17,
  ),
  myLocationEnabled: true,
  markers: _markers,
  myLocationButtonEnabled: false,
  onMapCreated: (GoogleMapController googleMap) {
    _googleMapController.complete(googleMap);
  },
  polygons: rhombusPolygon(),
);

// ひし形のポリゴンを表示
Set<Polygon> rhombusPolygon() {
  const lat = 34.59370; // 値は適当です
  const lng = 135.53810; // 値は適当です
  const offSet = 0.001;
  final polygonCords = <LatLng>[];

  polygonCords.add(LatLng(lat, lng - offSet));
  polygonCords.add(LatLng(lat + offSet, lng));
  polygonCords.add(LatLng(lat, lng + offSet));
  polygonCords.add(LatLng(lat - offSet, lng));
  polygonCords.add(LatLng(lat, lng - offSet));

  final polygonSet = <Polygon>{};
  polygonSet.add(Polygon(
    polygonId: PolygonId('test'),
    points: polygonCords,
    strokeWidth: 1,
    strokeColor: Colors.blue,
    fillColor: Colors.lightBlue.withOpacity(0.1),
  ));

  return polygonSet;
}
```
- **polylines** 

Set&lt;Polyline&gt;
マップ上の区間に線を表示します。
区間は緯度と経度で指定します。
ルート案内などで表示される出発点から到着点までの経路などに使います。

```dart

GoogleMap(
  initialCameraPosition: CameraPosition(
    target: LatLng(latitude, longitude),
    zoom: 17,
  ),
  myLocationEnabled: true,
  markers: _markers,
  myLocationButtonEnabled: false,
  onMapCreated: (GoogleMapController googleMap) {
    _googleMapController.complete(googleMap);
  },
  polylines: testPolyline(),
);

Set<Polyline> testPolyline() {
  final _polyLine = <Polyline>[];

  _polyLine.add(Polyline(
    polylineId: PolylineId('route'),
    visible: true,
    color: Colors.blue,
    patterns: [PatternItem.dash(20), PatternItem.gap(10)],
      width: 3,
    points: const [
      LatLng(latitude, latitude), // 始点
      LatLng(latitude2, latitude2), // 終点
    ],
  ),
);
```

## GoogleMapController class

- **animateCamera（CameraUpdate cameraUpdate）**
カメラの位置をアニメーションにより変更します。
タップしたマーカーにギュイーンとアニメーションで移動するときに使います。

- **moveCamera（CameraUpdate cameraUpdate）**
Future<void>
カメラの位置を変更します。
animateCameraのアニメーションが必要ない時はこちらを使用します。
使い方はanimateCameraと同様です。

```dart

// 選択した場所を表示とzoomを設定
Future<void> _changePosition(LatLng latLng, double zoom) async {
  if (latLng != null) {
    final completer = await _googleMapController.future;
    final cameraPosition = CameraPosition(target: latLng, zoom: zoom);
    await completer
        .animateCamera(CameraUpdate.newCameraPosition(cameraPosition));
  }
}
```

- **hideMarkerInfoWindow（MarkerId markerId）** 
Future&lt;null&gt;
マーカーのInfoWindowを非表示にします。

- **showMarkerInfoWindow（MarkerId markerId）**
Future&lt;null&gt;
マーカーのInfoWindowを表示します。

- **isMarkerInfoWindowShown（MarkerId markerId）** 
Future&lt;bool&gt;
InfoWindowが表示されているときにTrueが返ります。
showMarkerInfoWindowやhideMarkerInfoWindowのメソッドと一緒に使うとInfoWindowの表示/非表示を制御できます。
↓多分これで動くはず....（ちゃんと検証できてないですゴメンなさい）

```dart

final Completer<GoogleMapController> _googleMapController =
    Completer<GoogleMapController>();

  // 選択したinfoViewを出す
void _updateSelectedPlace(OneDayJobDetail newJobs) {
  _infoWindowForSelectedPlace();
  setState(() {
    selectedPlace = newJobs;
  });
  _infoWindowForSelectedPlace();
}

Future<void> _infoWindowForSelectedPlace() async {
  if (selectedPlace != null && _googleMapController.isCompleted) {
    final selectedPlaceMarker = MarkerId(selectedPlace.id.value);
    final googleMap = await _googleMapController.future;
    final isSelectedPlaceMarkerShown =
        await googleMap.isMarkerInfoWindowShown(selectedPlaceMarker);
    if (!isSelectedPlaceMarkerShown) {
      await googleMap.hideMarkerInfoWindow(selectedPlaceMarker);
    } else {
      await googleMap.showMarkerInfoWindow(selectedPlaceMarker);
    }
  }
}
```

- **takeSnapshot（）** 

Future&lt;Uint8List&gt;
マップの画像byteが返ってきます。
GoogleMap内ではなく、ボタンをタップするとその時表示されているGoogleMapのsnapshotをとる感じで実装します。

```dart
FlatButton(
  child: const Text('snapshotとるー'),
  onPressed: () async {

    // Completerを使用する場合はこんな感じ
    final googleMap = await _googleMapController.future;
    final imageBytes = await googleMap.takeSnapshot();

    // GoogleMapControllerの場合はこんな感じで
    // final imageBytes = await _mapController?.takeSnapshot();
    setState(() {
      _imageBytes = imageBytes;
    });
  },
),
Container(
  decoration: BoxDecoration(color: Colors.blueGrey[50]),
  height: 180,
  // ここに写真が表示される
  child: _imageBytes != null ? Image.memory(_imageBytes) : null,
),
```
# さいごに
全体のコードをここに載せようと思いましたが、時間が足りずできませんでした。ｽﾐﾏｾﾝ...
いつもはその場限りで、何度もドキュメントを見にいって同じ事を調べていましたが、今回ちゃんとまとめる事ができ理解も深まったので今後、開発中に調べる手間が無くなればと思います。

あまり大した事が書けませんでしたが、これからFlutterを始める初心者の方のお役に立てれば幸いです！

もし、間違いなどがありましたらご教授ください。


# 参考文献
- [GoogleMap class](https://pub.dev/documentation/google_maps_flutter/latest/google_maps_flutter/GoogleMap-class.html)
- [GoogleMapController class](https://pub.dev/documentation/google_maps_flutter/latest/google_maps_flutter/GoogleMapController-class.html)
- [FlutterでGoogle Mapを使ってできること、できないこと（2019年版）](https://qiita.com/YusukeIwaki/items/a9d62459cbbc8df1dfa5)