From 0d7cf417a3d776717509a54fbd57e182d222a4d8 Mon Sep 17 00:00:00 2001 From: SotaTamura Date: Sat, 18 Jul 2026 18:10:49 +0900 Subject: [PATCH] =?UTF-8?q?=E7=8F=BE=E5=9C=A8=E4=BD=8D=E7=BD=AE=E3=81=AE?= =?UTF-8?q?=E8=A1=A8=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- android/app/src/main/AndroidManifest.xml | 2 + ios/Runner/Info.plist | 4 + lib/features/map/presentation/map_screen.dart | 114 ++++++++++++++++++ 3 files changed, 120 insertions(+) diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 4f59f42..6a90b27 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -1,4 +1,6 @@ + + + NSLocationWhenInUseUsageDescription + 現在地を表示するために位置情報を使用します。 + NSLocationAlwaysAndWhenInUseUsageDescription + 現在地を表示するために位置情報を使用します。 CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) CFBundleDisplayName diff --git a/lib/features/map/presentation/map_screen.dart b/lib/features/map/presentation/map_screen.dart index 01ba5a5..f997b59 100644 --- a/lib/features/map/presentation/map_screen.dart +++ b/lib/features/map/presentation/map_screen.dart @@ -13,6 +13,7 @@ import 'package:memomap/features/map/presentation/widgets/controls.dart'; import 'package:memomap/features/map/presentation/widgets/map_search_bar.dart'; import 'package:memomap/features/map/presentation/widgets/pin_list.dart'; import 'package:mapbox_maps_flutter/mapbox_maps_flutter.dart'; +import 'package:geolocator/geolocator.dart' as geo; import 'package:flutter/services.dart'; import 'dart:math' as math; import 'dart:convert'; @@ -79,10 +80,116 @@ class _MapScreenState extends ConsumerState { _updatePins(); _updateMapBounds(); + await _checkAndEnableLocation(); setState(() {}); } + Future _checkAndEnableLocation() async { + final permission = await geo.Geolocator.checkPermission(); + if (permission == geo.LocationPermission.whileInUse || + permission == geo.LocationPermission.always) { + await _enableLocationPuck(); + + try { + final position = await geo.Geolocator.getLastKnownPosition() ?? + await geo.Geolocator.getCurrentPosition( + locationSettings: const geo.LocationSettings( + accuracy: geo.LocationAccuracy.high, + ), + ); + + if (_mapboxMap != null) { + await _mapboxMap!.setCamera( + CameraOptions( + center: Point( + coordinates: Position(position.longitude, position.latitude), + ), + zoom: 15.0, + ), + ); + await _updateMapBounds(); + } + } catch (e) { + debugPrint("Error getting startup location: $e"); + } + } + } + + Future _enableLocationPuck() async { + if (_mapboxMap == null) return; + await _mapboxMap!.location.updateSettings( + LocationComponentSettings( + enabled: true, + pulsingEnabled: true, + ), + ); + } + + Future _moveToCurrentLocation() async { + bool serviceEnabled = await geo.Geolocator.isLocationServiceEnabled(); + if (!serviceEnabled) { + if (mounted) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('位置情報サービスが無効になっています。設定から有効にしてください。')), + ); + } + return; + } + + geo.LocationPermission permission = await geo.Geolocator.checkPermission(); + if (permission == geo.LocationPermission.denied) { + permission = await geo.Geolocator.requestPermission(); + if (permission == geo.LocationPermission.denied) { + if (mounted) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('位置情報の権限が拒否されました。')), + ); + } + return; + } + } + + if (permission == geo.LocationPermission.deniedForever) { + if (mounted) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text('位置情報の権限が永久に拒否されています。設定画面から許可してください。'), + ), + ); + } + return; + } + + await _enableLocationPuck(); + + try { + final position = await geo.Geolocator.getCurrentPosition( + locationSettings: const geo.LocationSettings( + accuracy: geo.LocationAccuracy.high, + ), + ); + + if (_mapboxMap != null) { + await _mapboxMap!.setCamera( + CameraOptions( + center: Point( + coordinates: Position(position.longitude, position.latitude), + ), + zoom: 15.0, + ), + ); + await _updateMapBounds(); + } + } catch (e) { + if (mounted) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text('現在地の取得に失敗しました: $e')), + ); + } + } + } + void _onPinListExtentChanged(double extent) { if ((_pinListExtent - extent).abs() < 0.001) return; setState(() { @@ -663,6 +770,13 @@ class _MapScreenState extends ConsumerState { child: Column( mainAxisSize: MainAxisSize.min, children: [ + FloatingActionButton( + heroTag: 'my_location', + onPressed: _moveToCurrentLocation, + tooltip: 'Current location', + child: const Icon(Icons.my_location), + ), + const SizedBox(height: 8), FloatingActionButton( heroTag: 'zoom_in', onPressed: () async {