FlutterでGoogle Mapを表示してみた

Flutterで、Google Mapを表示してみました。
2018.08.12

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

大阪オフィスの山田です。前回から引き続いて、楽しくFlutterいじりしているこの頃です。今回はGoogle Mapを表示してみようと思います。

開発環境

flutter doctorの結果です。

Doctor summary (to see all details, run flutter doctor -v):
[✓] Flutter (Channel beta, v0.5.1, on Mac OS X 10.13.5 17F77, locale ja-JP)
[✓] Android toolchain - develop for Android devices (Android SDK 28.0.1)
[✓] iOS toolchain - develop for iOS devices (Xcode 9.3)
[✓] Android Studio (version 3.1)
    ✗ Flutter plugin not installed; this adds Flutter specific functionality.
    ✗ Dart plugin not installed; this adds Dart specific functionality.
[!] VS Code (version 1.25.1)
[✓] Connected devices (2 available)

VSCodeの警告が出ていますが、Extensionインストール済みです。

Flutterのバージョンは以下の通りです。

Flutter 0.5.1 • channel beta • https://github.com/flutter/flutter.git
Framework • revision c7ea3ca377 (2 months ago) • 2018-05-29 21:07:33 +0200
Engine • revision 1ed25ca7b7
Tools • Dart 2.0.0-dev.58.0.flutter-f981f09760

この次ぐらいにはFlutterをバージョンアップしよう。

作る画面

本当に地図を表示して閉じるだけの画面です。

必要な準備

以下の準備が必要です。

  • map_viewパッケージを入れる
  • Google MapのAPIKeyを作成する
  • iOSとAndroid、それぞれでパーミッションを設定する
  • map_viewパッケージを入れる

    map_viewというパッケージを入れます。 2018/08/09時点で最新バージョンの0.0.14を入れます。pubspec.yamlのdependenciesに以下の行を追加します。

    map_view: ^0.0.14

    VSCodeを使っていると、pubspec.yamlを更新したら自動で取得してくれますね。便利。自動で取得できない場合は、flutter packages getコマンドを打ちましょう。

    Google MapのAPIキーを作成する

    こちらのページにて作成します。 以下の二つのAPIを有効にします。

  • Maps SDK for iOS
  • Maps SDK for Android
  • 有効にしたら認証情報を作成し、APIキーを入手しましょう。これでGoogle Mapを利用する準備が整いました。

    iOSでパーミッション設定をする

    ios/Runner/Info.plistを編集します。Xcodeでプロジェクトを開いて編集しました。以下の記述を追加します。

    <key>NSLocationWhenInUseUsageDescription</key>
    <string>Using location to display on a map</string>

    Androidでパーミッション設定をする

    1 android/app/src/main配下にあるAndroidManifest.xmlに以下の記述を追加します。

    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>

    2 続いて同ファイルのapplicationタグの内側に以下の記述を追加します。your_api_keyには、作成したAPIキーを設定してください。

    <meta-data android:name="com.google.android.maps.v2.API_KEY" android:value="your_api_key"/>
    <meta-data android:name="com.google.android.gms.version" android:value="@integer/google_play_services_version"/>

    3 続いて同ファイルに以下の記述を追加します。

    <activity android:name="com.apptreesoftware.mapview.MapActivity" android:theme="@style/Theme.AppCompat.Light.DarkActionBar"/>

    4 android/build.gradleファイルのbuildScript -> dependenciesの中に以下の記述を追加します。

    classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.1.2-4'

    ※KotlinでProjectを作っていたことが原因かわかりませんが、私の環境ではすでにkotlin-gradle-pluginの記述があったので、手順4については省略しました。

    実装

    まず、APIキーを設定します。今回はmainメソッドの中で設定しました。

    void main() {
      MapView.setApiKey("your_api_key");
      runApp(new MyApp());
    }

    次に、地図を表示するボタンを配置します。ボタンがタップされたのを検知し、後述する_showMapメソッドをコールします。

      var _mapView = MapView();
      
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text("Map"),
          ),
          body: Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.spaceEvenly,
              children: [
                RaisedButton(
                  child: Text('Show Map modal.'),
                  onPressed: () { _showMap(); },
                ),
              ],
            ),
          ),
        );
      }

    次は、いよいよ地図を表示します。モーダルで表示されます。

     void _showMap() {
        _mapView.show(new MapOptions(
          showUserLocation: true, 
          hideToolbar: false,
          showMyLocationButton: true,
          showCompassButton: true,
          ),
          toolbarActions: [new ToolbarAction("Close", 1)]
        );
        _mapView.onToolbarAction.listen((id) {
          if (id == 1) { 
            _mapView.dismiss();
          }
        });
      }

    表示するときにはMapOptionsを指定して表示します。現在地へ移動するボタンやコンパスボタンを表示するよ、などの設定を入れています。見たまんまですね。onToolbarActionをlistenすることで、タップされたらmapViewを閉じてます。

    おまけ

    開いた時、現在地を中心に地図が表示されてほしい。

    こんな感じに。  

    すんなりいくのかなと思いきや、ちょっとハマりました。探すと似たような内容がGithubのIssueに上がっているのですが、読んだ結果...

    (´・ω・`)???

    だったので、なんとなくこういう風にしたら実現できたという(ちょっと無理矢理ですが)実装を載せておきます。

    Location userLocation;
    
    // _showMapメソッドの最後に以下の処理を追加
    _mapView.onMapReady.listen((_) {
      if (userLocation != null) {
        _mapView.setCameraPosition(
          new CameraPosition(new Location(userLocation.latitude, userLocation.longitude), 14.0)
        );
      }
    });
    
    _mapView.onLocationUpdated.listen((location) {
      if (userLocation == null) {
        userLocation = location;
        _mapView.setCameraPosition(
          new CameraPosition(new Location(userLocation.latitude, userLocation.longitude), 14.0)
        );
      }
    });

    Locationが更新?されたタイミングでLocationを覚えておきます。初めて取得する場合は、mapのカメラポジションも移動させます。2回目開いた時はそのままそのポジションを使います。うーん。。。なんかもうちょい良い方法考えた方が良さそうですが、一旦ここまで。

    最後に

    如何だったでしょうか。Flutterいじりは楽しいのでこれからもちょくちょくブログを書くと思います。