こちらの記事を元に、同じこと(+α)を Mapbox で試してみました。
- Flutter で Google Map に現在位置を表示する(kurararara さん)
準備
まず Mapbox のアカウントを登録し、アクセストークンとシークレットトークンを取得したのち、必要に応じて地図の日本語化を行っておきます。
【参考】
- Google Flutter コラム- 第2 回 GoogleMapとmapboxの地図表示を比較してみた(日本システム開発/Google Flutter コラム)
- mapbox 日本語化の仕方(半力投球)
flutter create 【アプリケーション名】
でアプリケーションを作成し、以下のファイルを編集します。
2021/10/29 追記:
mapbox_gl 0.13.0
がリリースされ、Android で日本語の地図が表示できるようになったのと、誤記があったため一部修正しました。
※Flutter SDK 2.5.3 にて実行(macOS 11.6)。
pubspec.yaml
(関連部分のみ)
dependencies:
flutter:
sdk: flutter
mapbox_gl: ^0.13.0
location: ^4.3.0
android/build.gradle
(Android の場合・関連部分のみ)
allprojects {
repositories {
(中略)
maven {
url 'https://api.mapbox.com/downloads/v2/releases/maven'
authentication {
basic(BasicAuthentication)
}
credentials {
// Do not change the username below.
// This should always be `mapbox` (not your username).
username = 'mapbox'
// Use the secret token you stored in gradle.properties as the password
password = project.properties['MAPBOX_DOWNLOADS_TOKEN'] ?: ""
}
}
}
}
android/app/build.gradle
(Android の場合・関連部分のみ)
android {
(中略)
defaultConfig {
(中略)
minSdkVersion 21
(中略)
}
buildTypes {
release {
// other configs
ndk {
abiFilters 'armeabi-v7a','arm64-v8a','x86_64', 'x86'
}
}
}
android/app/src/AndroidManifest.xml
(Android の場合・関連部分のみ)
<meta-data
android:name="com.mapbox.token"
android:value="【Mapboxのアクセストークン】"
/>
※<application>
〜</application>
の間に追記。
android/gradle.properties
(Android の場合・関連部分のみ)
MAPBOX_DOWNLOADS_TOKEN=【Mapboxのシークレットトークン】
Android の場合、環境変数SDK_REGISTRY_TOKEN
にも Mapbox のシークレットトークンをセットしておきます(flutter run
の際に必要なので、面倒なら.zshrc
に保存するなどしてください)。
export SDK_REGISTRY_TOKEN="【Mapboxのシークレットトークン】"
ios/Runner/Info.plist
(iOS の場合・関連部分のみ)
<key>NSLocationAlwaysUsageDescription</key>
<string>Your location is required for this app</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>Your location is required for this app</string>
<key>MGLMapboxAccessToken</key>
<string>【Mapboxのアクセストークン】</string>
※<dict>
〜</dict>
の間に追記。
なお、デバッグ(開発)環境のみ以下の項目も必要になります。デバッグ環境とリリース環境のInfo.plist
の切り替え設定を行った後(例:Info-Debug.plist
とInfo-Release.plist
に分離して使い分ける)、デバッグ環境用のInfo.plist
のみに以下を追記します。
<key>NSBonjourServices</key>
<array>
<string>_dartobservatory._tcp.</string>
</array>
※<dict>
〜</dict>
の間に追記。
.netrc
(home ディレクトリへ)
こちらはアプリケーションのディレクトリではなく自分の home ディレクトリ(/Users/【ユーザ名】
)直下に配置します。
machine api.mapbox.com
login mapbox
password 【Mapboxのシークレットトークン】
ここまで準備できたら、flutter pub get
します。
コード例
最初に示した **kurararara さんの記事**のコードをベースに、mapbox_gl
(執筆時点で 0.12.0)に合わせて書き換えてあります。
また、以下の処理を追加実装しました。
- GPS の位置情報に合わせて地図を移動(地図の中心を常に現在位置に)
- 地図上のどこかをタップすると GPS への追従を停止
- 右下の GPS アイコンボタンのクリックで GPS 追従と停止を切り替え
GPS への追従はMyLocationTrackingMode.TrackingGPS
を使うと実装できそうな気がしたのですがうまくいかなかったので、location
で取得した緯度経度の変化をmoveCamera
を使って反映しています。
なお、できればanimateCamera
を使いたかったのですが、私が所有する Android(10)機ではうまく動きませんでした(iOS 14.8 では正しく動きました)。
import 'dart:async';
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:location/location.dart';
import 'package:mapbox_gl/mapbox_gl.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return const MaterialApp(
title: 'Flutter Mapbox',
home: MapPage(),
);
}
}
class MapPage extends StatefulWidget {
const MapPage({Key? key}) : super(key: key);
@override
_MapPageState createState() => _MapPageState();
}
class _MapPageState extends State<MapPage> {
final Completer<MapboxMapController> _controller = Completer();
final Location _locationService = Location();
// 地図スタイル用 Mapbox URL
final String _style = '【スタイルのURL】'; // 地図を日本語化したときなどに必要
// Location で緯度経度が取れなかったときのデフォルト値
final double _initialLat = 35.6895014;
final double _initialLong = 139.6917337;
// 現在位置
LocationData? _yourLocation;
// GPS 追従?
bool _gpsTracking = false;
// 現在位置の監視状況
StreamSubscription? _locationChangedListen;
@override
void initState() {
super.initState();
// 現在位置の取得
_getLocation();
// 現在位置の変化を監視
_locationChangedListen =
_locationService.onLocationChanged.listen((LocationData result) async {
setState(() {
_yourLocation = result;
});
});
setState(() {
_gpsTracking = true;
});
}
@override
void dispose() {
super.dispose();
// 監視を終了
_locationChangedListen?.cancel();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: _makeMapboxMap(),
floatingActionButton: _makeGpsIcon(),
);
}
Widget _makeMapboxMap() {
if (_yourLocation == null) {
// 現在位置が取れるまではロード中画面を表示
return const Center(
child: CircularProgressIndicator(),
);
}
// GPS 追従が ON かつ地図がロードされている→地図の中心を移動
if (_gpsTracking) {
_controller.future.then((mapboxMap) {
mapboxMap.moveCamera(CameraUpdate.newLatLng(LatLng(
_yourLocation!.latitude ?? _initialLat,
_yourLocation!.longitude ?? _initialLong)));
});
}
// Mapbox ウィジェットを返す
return MapboxMap(
// 地図(スタイル)を指定(デフォルト地図の場合は省略可)
styleString: _style,
// 初期表示される位置情報を現在位置から設定
initialCameraPosition: CameraPosition(
target: LatLng(_yourLocation!.latitude ?? _initialLat,
_yourLocation!.longitude ?? _initialLong),
zoom: 13.5,
),
onMapCreated: (MapboxMapController controller) {
_controller.complete(controller);
},
compassEnabled: true,
// 現在位置を表示する
myLocationEnabled: true,
// 地図をタップしたとき
onMapClick: (Point<double> point, LatLng tapPoint) {
_controller.future.then((mapboxMap) {
mapboxMap.moveCamera(CameraUpdate.newLatLng(tapPoint));
});
setState(() {
_gpsTracking = false;
});
},
);
}
Widget _makeGpsIcon() {
return FloatingActionButton(
backgroundColor: Colors.blue,
onPressed: () {
_gpsToggle();
},
child: Icon(
// GPS 追従の ON / OFF に合わせてアイコン表示する
_gpsTracking ? Icons.gps_fixed : Icons.gps_not_fixed,
),
);
}
void _gpsToggle() {
setState(() {
_gpsTracking = !_gpsTracking;
});
// ここは iOS では不要
if (_gpsTracking) {
_controller.future.then((mapboxMap) {
mapboxMap.moveCamera(CameraUpdate.newLatLng(LatLng(
_yourLocation!.latitude ?? _initialLat,
_yourLocation!.longitude ?? _initialLong)));
});
}
}
void _getLocation() async {
_yourLocation = await _locationService.getLocation();
}
}
※デバッグ環境なので、自宅がバレないように(?)GPS 追従を停止した状態の画面です。
補足
**【参考】の最初の記事**にも書かれていますが、mapbox_gl
のバージョン 0.12.0 では Android で日本語化した地図の日本語部分の表示ができない問題があります。
2021/10/29 追記:
前述のとおり、mapbox_gl 0.13.0
がリリースされ Android で日本語の地図が表示できるようになりました。
MyLocationRenderMode
のように Android は対応しているのに iOS が未対応の機能もありますが、現状、コード例に示した範囲では iOS のほうが機能の対応が進んでいる印象を受けます。
2021/09/24 追記:
続編を書きました。