Help us understand the problem. What is going on with this article?

Flutter+Google Maps APIで現在地表示

はじめに

FlutterでGoogle Maps APIを用いて現在地を表示するのに結構苦労したのでメモ代わりに残しておこうと思います。現在地取得、現在地表示についてを見たい方はここを押してください。

APIキーの取得

APIキー取得まではGoogle公式Flutter用Google Mapsプラグインを一通り使ってみたを参考にしてください。

Google Mapsを表示

pubspec.yamlにgoogle_maps_flutterを追加する

pubspec.yaml
dependencies:
  flutter:
    sdk: flutter

  # The following adds the Cupertino Icons font to your application.
  # Use with the CupertinoIcons class for iOS style icons.
  cupertino_icons: ^0.1.2
  google_maps_flutter: ^0.5.13 // 追加


dev_dependencies:
  flutter_test:
    sdk: flutter

google_maps_flutterを追加し、flutter pub getする。

Android用の設定

android/app/src/main/AndroidManifest.xmlに以下を追記する

android/app/src/main/AndroidManifest.xml
    <application
        android:name="io.flutter.app.FlutterApplication"
        android:label="flutter_maps"
        android:icon="@mipmap/ic_launcher">
        <meta-data android:name="com.google.android.geo.API_KEY" // 追加
            android:value="取得したAPIキー"/>                       // 追加
        <activity
            android:name=".MainActivity"
       ...

iOS用の設定

ios/Runner/Info.plistを開いて以下を追記します

ios/Runner/Info.plist
<array>
        <string>UIInterfaceOrientationPortrait</string>
        <string>UIInterfaceOrientationPortraitUpsideDown</string>
        <string>UIInterfaceOrientationLandscapeLeft</string>
        <string>UIInterfaceOrientationLandscapeRight</string>
</array>
    <key>UIViewControllerBasedStatusBarAppearance</key>
    <false/>
<key>io.flutter.embedded_views_preview</key> // 追加
<string>YES</string>                         // 追加

ios/Runner/AppDelegate.mに以下を追記します

ios/Runner/AppDelegate.m
#include "AppDelegate.h"
#include "GeneratedPluginRegistrant.h"
// Add the GoogleMaps import.
#import "GoogleMaps/GoogleMaps.h" // 追加

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application
    didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
  [GMSServices provideAPIKey:@"取得したAPIキー"];                      // 追加
  [GeneratedPluginRegistrant registerWithRegistry:self];
  // Override point for customization after application launch.
  return [super application:application didFinishLaunchingWithOptions:launchOptions];
}

main.dart, google_maps.dartのコード(コピペでOK)

lib/main.dart
import 'package:flutter/material.dart';
import 'google_maps.dart';

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

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Maps',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: GoogleMaps(),
    );
  }
}
lib/google_maps.dart
import 'dart:async';

import 'package:flutter/material.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';

class GoogleMaps extends StatefulWidget {
  @override
  _GoogleMapsState createState() => _GoogleMapsState();
}

class _GoogleMapsState extends State<GoogleMaps> {
  Completer<GoogleMapController> _controller = Completer();

  void _onMapCreated(GoogleMapController controller) {
    _controller.complete(controller);
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text("Flutter Maps"),
        ),
        body: GoogleMap(
          onMapCreated: _onMapCreated,
          initialCameraPosition: CameraPosition(  // 最初のカメラ位置
            target: LatLng(34.643208, 134.997586),
            zoom: 17.0,
          ),
        ),
      ),
    );
  }
}

スクリーンショット

Android

スクリーンショット 2019-05-28 14 53 17
※実機の関係でこの記事ではスクリーンショットはAndroidだけとします。

現在地表示

pubspec.yamlにlocationを追加します

pubspec.yaml
dependencies:
  flutter:
    sdk: flutter

  # The following adds the Cupertino Icons font to your application.
  # Use with the CupertinoIcons class for iOS style icons.
  cupertino_icons: ^0.1.2
  google_maps_flutter: ^0.5.13
  location: ^2.3.5 // 追加

locationを追加し、flutter pub getする。

Android用の設定

android/build.gradleを開いて以下に変更します

android/build.gradle
buildscript {
    repositories {
        google()
        jcenter()
    }

    dependencies {
        classpath 'com.android.tools.build:gradle:3.3.0' // 変更
        classpath 'com.google.gms:google-services:4.2.0' // 追加
    }
}

android/gradle.propertiesに以下を追記します

android/gradle.properties
android.enableJetifier=true   // 追加
android.useAndroidX=true      // 追加
org.gradle.jvmargs=-Xmx1536M

android/app/src/main/AndroidManifest.xmlに以下を追記します

android/app/src/main/AndroidManifest.xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.butachin.flutter_maps">

    <uses-permission android:name="android.permission.INTERNET"/>              // 追加
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/> // 追加

    <!-- io.flutter.app.FlutterApplication is an android.app.Application that
         calls FlutterMain.startInitialization(this); in its onCreate method.
         In most cases you can leave this as-is, but you if you want to provide
         additional functionality it is fine to subclass or reimplement
         FlutterApplication and put your custom class here. -->
    <application
        android:name="io.flutter.app.FlutterApplication"
        android:label="flutter_maps"

android.permission.ACCESS_FINE_LOCATIONを追加することによって位置情報の権限を付与することができる。android.permission.INTERNETは追加しなくても良いと思う。

iOS用の設定

ios/Runner/Info.plistを開いて以下を追記します

ios/Runner/Info.plist
    </array>
    <key>UIViewControllerBasedStatusBarAppearance</key>
    <false/>
    <key>io.flutter.embedded_views_preview</key>
    <string>YES</string>
    <key>NSLocationAlwaysUsageDescription</key>             // 追加
    <string>Your location is required for this app</string> // 追加
    <key>NSLocationWhenInUseUsageDescription</key>          // 追加
    <string>Your location is required for this app</string> // 追加
</dict>
</plist>

google_maps.dartの変更, 追加

lib/google_maps.dart
class _GoogleMapsState extends State<GoogleMaps> {
  LocationData currentLocation;                                              // 追加
  // StreamSubscription<LocationData> locationSubscription;

  Location _locationService = new Location();                                 // 追加
  String error;                                                               // 追加

  @override                                                                   // 追加
  void initState() {                                                          // 追加
    super.initState();                                                        // 追加

    initPlatformState();                                                      // 追加
    _locationService.onLocationChanged().listen((LocationData result) async { // 追加
      setState(() {                                                           // 追加
        currentLocation = result;                                             // 追加
      });                                                                     // 追加
    });                                                                       // 追加
  }                                                                           // 追加

  Completer<GoogleMapController> _controller = Completer();

  void _onMapCreated(GoogleMapController controller) {
  ...
  initialCameraPosition: CameraPosition(
            target: LatLng(currentLocation.latitude, currentLocation.longitude), // 緯度経度を取得したものに変更
            zoom: 17.0,
 ...
  void initPlatformState() async {
    LocationData myLocation;
    try {
      myLocation = await _locationService.getLocation();
      error = "";
    }on PlatformException catch(e){
      if(e.code == 'PERMISSION_DENITED')
        error = 'Permission denited';
      else if(e.code == 'PERMISSION_DENITED_NEVER_ASK')
        error = 'Permission denited - please ask the user to enable it from the app settings';
      myLocation = null;
    }
    setState(() {
      currentLocation = myLocation;
    });
  }
}

initPlatformState()は全てコピペで追加してください。

スクリーンショット

Android

スクリーンショット 2019-05-28 14 52 54

このままのコードだと、一瞬ですがこの赤い画面が表示されると思います。これは現在地を取得できていないよというエラーなので取得するまで待つようにしたいと思います。

※もしこの画面がずっと出ていたら、AndroidならAndroidmanifest.xmlのandroid.permission.ACCESS_FINE_LOCATIONが、iOSならNSLocationWhenInUseUsageDescription, NSLocationAlwaysUsageDescriptionがうまく反映されていないかもしれないです。再起動したりpubspec.lockを消してpub getし直したりしてみると直るかも。ちなみに私はどうしても権限が付与できなくて、GitHubにあげたコードをCloneしてきたら動きました。

google_maps.dartに追加

lib/google_maps.dart
@override
  Widget build(BuildContext context) {
    if (currentLocation == null) {          // 追加
      return Center(                        // 追加
        child: CircularProgressIndicator(), // 追加
      );                                    // 追加
    } else {                                // 追加
      return MaterialApp(

上記を追加することにより、赤い画面を出すことなく現在地を表示することができます。

まとめ

今回はFlutterの現在地を取得する記事があまりなかったため調べて実装したことをメモ程度に書きました。Flutterを始めて、Google Maps APIを触ってみようかなくらいの人の助けになれば嬉しいです。

ソースコード

https://github.com/butachin/flutter_maps

参考

Google公式Flutter用Google Mapsプラグインを一通り使ってみた

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした