🗺️
【Flutter】Riverpodを使用したGoogleMap上での現在位置表示の実装
はじめに
この記事ではRiverpodによる状態管理で、GoogleMapに自分の現在位置を表示する方法をまとめていきたいと思います。
ここではRiverpodに関連した部分だけに着目して話していくため、APIKeyの発行手順やGoogleMap導入の際に必要になってくるInfo.plistやAndroidManifest.xmlの設定は以下のサイトなどを参考に、事前に設定しておいて下さい。
使用したパッケージ
- google_maps_flutter: ^2.2.8
- flutter_riverpod: ^2.3.6
- geolocator: ^9.0.2
位置情報を返す方法
まずGeolocatorでは次のように、getCurrentPosition()を呼ぶことで位置情報を返すことができます。
return Geolocator.getCurrentPosition();
これを使用して、以下のlocation_provider.dartでは位置情報のパーミッションの許可を促した後に、許可されていたらGeolocatorで位置情報を返すコードの全文です。
location_provider.dart
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:geolocator/geolocator.dart';
final locationProvider = FutureProvider((ref) async {
bool serviceEnabled;
LocationPermission permission;
// 位置情報サービスが有効かどうかのテスト
serviceEnabled = await Geolocator.isLocationServiceEnabled();
if (!serviceEnabled) {
return null;
}
//位置情報サービスのパーミッションチェック
permission = await Geolocator.checkPermission();
if (permission == LocationPermission.denied) {
//位置情報にアクセスするための許可を促す
permission = await Geolocator.requestPermission();
if (permission == LocationPermission.denied) {
return null;
}
}
//拒否されている場合ここでエラーになる
if (permission == LocationPermission.deniedForever) {
return null;
}
//許可された場合、位置情報を返す
return Geolocator.getCurrentPosition();
});
マップ生成時にカメラを現在地に移動させる方法
Page側では、まず位置データを取得してその地点へカメラを移動させるメソッドを用意します。
ref を引数として受け取り、その ref を使ってプロバイダーにアクセスできます。
latitude又はlongitudeがnullだった場合returnしているのは、現在地の取得に失敗した際でもCameraUpdateをしてエラーになってしまわないようにするために必須です。
Future<void> moveCamera(WidgetRef ref) async {
position = await ref.refresh(locationProvider.future);
final mapController = await mapControllerCompleter.future;
final latitude = position?.latitude;
final longitude = position?.longitude;
if (latitude == null || longitude == null) {
return;
}
await mapController.moveCamera(
CameraUpdate.newCameraPosition(
CameraPosition(
target: LatLng(latitude, longitude),
zoom: 13,
),
),
);
}
そして、Widgetが初めてビルドされた後に、上記で作成した非同期関数 moveCamera を呼び出すようにしています。
これにより、Widgetが描画された後に一度だけ実行されることが保証されます。
WidgetsBinding.instance.addPostFrameCallback((_) async {
await moveCamera(ref);
});
最後に
ここまでのコードの全文です。
map_page.dart
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:geolocator/geolocator.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import '../provider/location_provider.dart';
class MapPage extends ConsumerWidget {
const MapPage({super.key});
Widget build(BuildContext context, WidgetRef ref) {
final mapControllerCompleter = Completer<GoogleMapController>();
Position? position;//null許容で宣言
Future<void> onMapCreated(GoogleMapController controller) async {
mapControllerCompleter.complete(controller);
}
// 位置データを取得し、カメラを移動させるメソッド
Future<void> moveCamera(WidgetRef ref) async {
position = await ref.refresh(locationProvider.future);
final mapController = await mapControllerCompleter.future;
final latitude = position?.latitude;
final longitude = position?.longitude;
if (latitude == null || longitude == null) {
return;
}
await mapController.moveCamera(
CameraUpdate.newCameraPosition(
CameraPosition(
target: LatLng(latitude, longitude),
zoom: 13,
),
),
);
}
// Widgetが初めてビルドされた後にこのメソッドを呼び出す
WidgetsBinding.instance?.addPostFrameCallback((_) async {
await moveCamera(ref);
});
return GoogleMap(
onMapCreated: onMapCreated,
initialCameraPosition: CameraPosition(
target: LatLng(
//positionがnullなら初期座標を東京付近に設定する
position?.latitude ?? 36,
position?.longitude ?? 140,
),
zoom: 16,
),
myLocationEnabled: true,
);
}
}
参考にさせていただいたサイト等
最後までご覧頂き、ありがとうございました。
不足事項等あれば教えていただけると幸いです。
お世話になっているコミュニティ
Discussion