はじめに
FlutterでGoogle Maps APIを用いて現在地を表示するのに結構苦労したのでメモ代わりに残しておこうと思います。現在地取得、現在地表示についてを見たい方はここを押してください。
APIキーの取得
APIキー取得まではGoogle公式Flutter用Google Mapsプラグインを一通り使ってみたを参考にしてください。
Google Mapsを表示
pubspec.yamlにgoogle_maps_flutterを追加する
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に以下を追記する
<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を開いて以下を追記します
<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に以下を追記します
#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)
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(),
);
}
}
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
※実機の関係でこの記事ではスクリーンショットはAndroidだけとします。現在地表示
pubspec.yamlにlocationを追加します
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を開いて以下に変更します
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.enableJetifier=true // 追加
android.useAndroidX=true // 追加
org.gradle.jvmargs=-Xmx1536M
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を開いて以下を追記します
</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の変更, 追加
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
このままのコードだと、一瞬ですがこの赤い画面が表示されると思います。これは現在地を取得できていないよというエラーなので取得するまで待つようにしたいと思います。
※もしこの画面がずっと出ていたら、AndroidならAndroidmanifest.xmlのandroid.permission.ACCESS_FINE_LOCATIONが、iOSならNSLocationWhenInUseUsageDescription, NSLocationAlwaysUsageDescriptionがうまく反映されていないかもしれないです。再起動したりpubspec.lockを消してpub getし直したりしてみると直るかも。ちなみに私はどうしても権限が付与できなくて、GitHubにあげたコードをCloneしてきたら動きました。
google_maps.dartに追加
@override
Widget build(BuildContext context) {
if (currentLocation == null) { // 追加
return Center( // 追加
child: CircularProgressIndicator(), // 追加
); // 追加
} else { // 追加
return MaterialApp(
上記を追加することにより、赤い画面を出すことなく現在地を表示することができます。
まとめ
今回はFlutterの現在地を取得する記事があまりなかったため調べて実装したことをメモ程度に書きました。Flutterを始めて、Google Maps APIを触ってみようかなくらいの人の助けになれば嬉しいです。
ソースコード