temporary update
This commit is contained in:
@ -418,7 +418,7 @@ class CameraPage extends StatelessWidget {
|
||||
},
|
||||
child: Text("finish_goal".tr))
|
||||
: const Center(
|
||||
child: CircularProgressIndicator(),
|
||||
child: CircularProgressIndicator(), // Camera page
|
||||
)
|
||||
: Container())
|
||||
],
|
||||
|
||||
@ -1126,7 +1126,7 @@ class DestinationController extends GetxController {
|
||||
}
|
||||
|
||||
Future<String?> _saveImageToGallery(String imagePath) async {
|
||||
final status = await PermissionController.checkStoragePermission();
|
||||
final status = await PermissionController.checkStoragePermission(); // destinationController._saveImageToGallery
|
||||
if(!status){
|
||||
await PermissionController.requestStoragePermission();
|
||||
}
|
||||
@ -1333,12 +1333,6 @@ class DestinationController extends GetxController {
|
||||
void onInit() async {
|
||||
super.onInit();
|
||||
|
||||
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
||||
await PermissionController.checkAndRequestPermissions();
|
||||
});
|
||||
|
||||
|
||||
startGPSCheckTimer();
|
||||
|
||||
// MapControllerの初期化完了を待機するフラグを設定
|
||||
@ -1645,6 +1639,7 @@ class DestinationController extends GetxController {
|
||||
final token = indexController.userToken;
|
||||
if (token != null && token.isNotEmpty) {
|
||||
await indexController.loadUserDetailsForToken(token);
|
||||
await Future.delayed(Duration(milliseconds: 500)); // 短い遅延を追加
|
||||
fixMapBound(token);
|
||||
}else {
|
||||
Get.toNamed(AppPages.LOGIN)!.then((value) {
|
||||
@ -1665,6 +1660,9 @@ class DestinationController extends GetxController {
|
||||
});
|
||||
}
|
||||
|
||||
// MapControllerの準備が整うまで待機
|
||||
await indexController.waitForMapControllerReady();
|
||||
|
||||
// 地図のイベントリスナーを設定
|
||||
indexController.mapController.mapEventStream.listen((MapEvent mapEvent) {
|
||||
if (mapEvent is MapEventMoveEnd) {
|
||||
@ -1677,11 +1675,12 @@ class DestinationController extends GetxController {
|
||||
|
||||
// 地図の境界を修正する関数です。
|
||||
//
|
||||
void fixMapBound(String token) {
|
||||
Future<void> fixMapBound(String token) async {
|
||||
await indexController.waitForMapControllerReady();
|
||||
//String _token = indexController.currentUser[0]["token"];
|
||||
indexController.switchPage(AppPages.INDEX);
|
||||
|
||||
if (isMapControllerReady) {
|
||||
if (indexController.isMapControllerReady.value) {
|
||||
LocationService.getLocationsExt(token).then((value) {
|
||||
if (value != null) {
|
||||
//print("--- loc ext is - $value ----");
|
||||
@ -1743,11 +1742,18 @@ class DestinationController extends GetxController {
|
||||
// 地図を現在位置に中央揃えする関数です。
|
||||
//
|
||||
void centerMapToCurrentLocation() {
|
||||
final indexController = Get.find<IndexController>();
|
||||
if (indexController.isMapControllerReady.value && indexController.isMapControllerReady.value) {
|
||||
// ... 現在位置へのセンタリングのロジック ...
|
||||
indexController.mapController.move(LatLng(currentLat, currentLon), 17.0);
|
||||
} else {
|
||||
debugPrint('Map controller is not ready for centering');
|
||||
}
|
||||
//print("center is ${currentLat}, ${currentLon}");
|
||||
// Akira ... 状況によって呼ぶか呼ばないか
|
||||
if (currentLat != 0 || currentLon != 0) {
|
||||
indexController.mapController.move(LatLng(currentLat, currentLon), 17.0);
|
||||
}
|
||||
//if (currentLat != 0 || currentLon != 0) {
|
||||
//indexController.mapController.move(LatLng(currentLat, currentLon), 17.0);
|
||||
//}
|
||||
}
|
||||
|
||||
// 接続状態が変更されたときに呼び出される関数です。
|
||||
@ -1920,7 +1926,7 @@ class DestinationController extends GetxController {
|
||||
barrierDismissible: false,
|
||||
builder: (BuildContext context) {
|
||||
return const Center(
|
||||
child: CircularProgressIndicator(),
|
||||
child: CircularProgressIndicator(), // Destination COntroller
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@ -27,6 +27,8 @@ class EntryController extends GetxController {
|
||||
final currentEntry = Rx<Entry?>(null);
|
||||
final isLoading = true.obs;
|
||||
|
||||
final activeEvents = <Event>[].obs; //有効なイベントリスト
|
||||
|
||||
@override
|
||||
void onInit() async {
|
||||
super.onInit();
|
||||
@ -52,14 +54,15 @@ class EntryController extends GetxController {
|
||||
fetchTeams(),
|
||||
fetchCategories(),
|
||||
]);
|
||||
updateActiveEvents(); // イベント取得後にアクティブなイベントを更新
|
||||
if (Get.arguments != null && Get.arguments['entry'] != null) {
|
||||
currentEntry.value = Get.arguments['entry'];
|
||||
initializeEditMode(currentEntry.value!);
|
||||
} else {
|
||||
// 新規作成モードの場合、最初のイベントを選択
|
||||
if (events.isNotEmpty) {
|
||||
selectedEvent.value = events.first;
|
||||
selectedDate.value = events.first.startDatetime;
|
||||
selectedEvent.value = activeEvents.first;
|
||||
selectedDate.value = activeEvents.first.startDatetime;
|
||||
}
|
||||
}
|
||||
} catch(e) {
|
||||
@ -70,6 +73,10 @@ class EntryController extends GetxController {
|
||||
}
|
||||
}
|
||||
|
||||
void updateActiveEvents() {
|
||||
final now = tz.TZDateTime.now(tz.getLocation('Asia/Tokyo'));
|
||||
activeEvents.assignAll(events.where((event) => event.deadlineDateTime.isAfter(now)));
|
||||
}
|
||||
|
||||
void initializeEditMode(Entry entry) {
|
||||
currentEntry.value = entry;
|
||||
@ -143,6 +150,7 @@ class EntryController extends GetxController {
|
||||
deadlineDateTime: deadlineDateTime,
|
||||
);
|
||||
}).toList());
|
||||
updateActiveEvents();
|
||||
} catch (e) {
|
||||
print('Error fetching events: $e');
|
||||
Get.snackbar('Error', 'Failed to fetch events');
|
||||
|
||||
@ -39,10 +39,10 @@ class EntryDetailPage extends GetView<EntryController> {
|
||||
children: [
|
||||
_buildDropdown<Event>(
|
||||
label: 'イベント',
|
||||
items: controller.events,
|
||||
items: controller.activeEvents,
|
||||
selectedId: controller.selectedEvent.value?.id,
|
||||
onChanged: (eventId) => controller.updateEvent(
|
||||
controller.events.firstWhere((e) => e.id == eventId)
|
||||
controller.activeEvents.firstWhere((e) => e.id == eventId)
|
||||
),
|
||||
getDisplayName: (event) => event.eventName,
|
||||
getId: (event) => event.id,
|
||||
|
||||
@ -7,6 +7,8 @@ import 'package:gifunavi/pages/entry/entry_controller.dart';
|
||||
import 'package:gifunavi/routes/app_pages.dart';
|
||||
import 'package:timezone/timezone.dart' as tz;
|
||||
|
||||
import '../../model/entry.dart';
|
||||
|
||||
class EntryListPage extends GetView<EntryController> {
|
||||
const EntryListPage({super.key});
|
||||
|
||||
@ -28,11 +30,35 @@ class EntryListPage extends GetView<EntryController> {
|
||||
child: Text('表示するエントリーがありません。'),
|
||||
);
|
||||
}
|
||||
|
||||
final sortedEntries = controller.entries.toList()
|
||||
..sort((b, a) => a.date!.compareTo(b.date!));
|
||||
|
||||
return ListView.builder(
|
||||
itemCount: controller.entries.length,
|
||||
itemCount: sortedEntries.length,
|
||||
itemBuilder: (context, index) {
|
||||
final entry = controller.entries[index];
|
||||
final entry = sortedEntries[index];
|
||||
final now = DateTime.now();
|
||||
final isEntryInFuture = _compareDatesOnly(entry.date!, now) >= 0;
|
||||
|
||||
//final isEntryInFuture = entry.date!.isAfter(now) || entry.date!.isAtSameMomentAs(now);
|
||||
|
||||
Widget? leadingIcon;
|
||||
if (!isEntryInFuture) {
|
||||
if (entry.hasParticipated) {
|
||||
if (entry.hasGoaled) {
|
||||
leadingIcon =
|
||||
const Icon(Icons.check_circle, color: Colors.green);
|
||||
} else {
|
||||
leadingIcon = const Icon(Icons.warning, color: Colors.yellow);
|
||||
}
|
||||
} else {
|
||||
leadingIcon = const Icon(Icons.cancel, color: Colors.red);
|
||||
}
|
||||
}
|
||||
|
||||
return ListTile(
|
||||
leading: leadingIcon,
|
||||
title: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
@ -49,9 +75,16 @@ class EntryListPage extends GetView<EntryController> {
|
||||
Text('ゼッケン: ${entry.zekkenNumber ?? "未設定"}'),
|
||||
],
|
||||
),
|
||||
onTap: () =>
|
||||
onTap: () {
|
||||
if (isEntryInFuture) {
|
||||
Get.toNamed(AppPages.ENTRY_DETAIL,
|
||||
arguments: {'mode': 'edit', 'entry': entry}),
|
||||
arguments: {'mode': 'edit', 'entry': entry});
|
||||
} else if (entry.hasParticipated) {
|
||||
Get.toNamed(AppPages.EVENT_RESULT, arguments: {'entry': entry});
|
||||
} else {
|
||||
_showNonParticipationDialog(context, entry);
|
||||
}
|
||||
}
|
||||
);
|
||||
},
|
||||
);
|
||||
@ -59,6 +92,11 @@ class EntryListPage extends GetView<EntryController> {
|
||||
);
|
||||
}
|
||||
|
||||
// 新しく追加するメソッド
|
||||
int _compareDatesOnly(DateTime a, DateTime b) {
|
||||
return DateTime(a.year, a.month, a.day).compareTo(DateTime(b.year, b.month, b.day));
|
||||
}
|
||||
|
||||
String _formatDate(DateTime? date) {
|
||||
if (date == null) {
|
||||
return '日時未設定';
|
||||
@ -66,6 +104,26 @@ class EntryListPage extends GetView<EntryController> {
|
||||
final jstDate = tz.TZDateTime.from(date, tz.getLocation('Asia/Tokyo'));
|
||||
return DateFormat('yyyy-MM-dd').format(jstDate);
|
||||
}
|
||||
|
||||
void _showNonParticipationDialog(BuildContext context, Entry entry) {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertDialog(
|
||||
title: Text(entry.event.eventName),
|
||||
content: Text('${_formatDate(entry.date)}\n\n不参加でした'),
|
||||
actions: <Widget>[
|
||||
TextButton(
|
||||
child: const Text('閉じる'),
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class EntryListPage_old extends GetView<EntryController> {
|
||||
@ -85,7 +143,7 @@ class EntryListPage_old extends GetView<EntryController> {
|
||||
),
|
||||
body: Obx((){
|
||||
if (controller.isLoading.value) {
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
return const Center(child: CircularProgressIndicator()); // EntryList
|
||||
}
|
||||
|
||||
// エントリーを日付昇順にソート
|
||||
|
||||
155
lib/pages/entry/event_result_page.dart
Normal file
155
lib/pages/entry/event_result_page.dart
Normal file
@ -0,0 +1,155 @@
|
||||
import 'dart:io';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:gifunavi/model/entry.dart';
|
||||
import 'package:gifunavi/pages/gps/gps_controller.dart';
|
||||
import 'package:gifunavi/pages/history/history_controller.dart';
|
||||
import 'package:flutter_map/flutter_map.dart';
|
||||
import 'package:latlong2/latlong.dart';
|
||||
import 'package:gifunavi/pages/gps/gps_controller.dart';
|
||||
import 'package:gifunavi/pages/history/history_controller.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
|
||||
class EventResultPage extends StatefulWidget {
|
||||
final Entry entry;
|
||||
|
||||
const EventResultPage({super.key, required this.entry});
|
||||
|
||||
@override
|
||||
State<EventResultPage> createState() => _EventResultPageState();
|
||||
|
||||
}
|
||||
|
||||
class _EventResultPageState extends State<EventResultPage> {
|
||||
late GpsController gpsController;
|
||||
late HistoryController historyController;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
gpsController = Get.put(GpsController());
|
||||
historyController = Get.put(HistoryController());
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return DefaultTabController(
|
||||
length: 3,
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text('${widget.entry.event.eventName} 結果'),
|
||||
bottom: const TabBar(
|
||||
tabs: [
|
||||
Tab(text: 'ランキング'),
|
||||
Tab(text: '走行経路'),
|
||||
Tab(text: 'チェックポイント'),
|
||||
],
|
||||
),
|
||||
),
|
||||
body: TabBarView(
|
||||
children: [
|
||||
_buildFutureTab(), //_buildRankingTab(),
|
||||
_buildFutureTab(), //_buildRouteTab(),
|
||||
_buildFutureTab(), //_buildCheckpointTab(),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildFutureTab() {
|
||||
// ランキングの表示ロジックを実装
|
||||
return const Center(child: Text('近日公開予定(当面、HPを参照ください。)'));
|
||||
}
|
||||
|
||||
|
||||
Widget _buildRankingTab() {
|
||||
// ランキングの表示ロジックを実装
|
||||
return const Center(child: Text('ランキング表示(実装が必要)'));
|
||||
}
|
||||
|
||||
Widget _buildRouteTab() {
|
||||
return Obx(() {
|
||||
if (gpsController.gpsData.isEmpty) {
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
}
|
||||
return FlutterMap(
|
||||
options: MapOptions(
|
||||
center: LatLng(gpsController.gpsData[0].lat, gpsController.gpsData[0].lon),
|
||||
zoom: 13.0,
|
||||
),
|
||||
children: [
|
||||
TileLayer(
|
||||
urlTemplate: "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",
|
||||
subdomains: ['a', 'b', 'c'],
|
||||
),
|
||||
PolylineLayer(
|
||||
polylines: [
|
||||
Polyline(
|
||||
points: gpsController.gpsData
|
||||
.map((data) => LatLng(data.lat, data.lon))
|
||||
.toList(),
|
||||
color: Colors.red,
|
||||
strokeWidth: 3.0,
|
||||
),
|
||||
],
|
||||
),
|
||||
MarkerLayer(
|
||||
markers: gpsController.gpsData
|
||||
.map((data) => Marker(
|
||||
width: 80.0,
|
||||
height: 80.0,
|
||||
point: LatLng(data.lat, data.lon),
|
||||
child: const Icon(Icons.location_on, color: Colors.red),
|
||||
))
|
||||
.toList(),
|
||||
),
|
||||
],
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
Widget _buildCheckpointTab() {
|
||||
return Obx(() {
|
||||
if (historyController.checkpoints.isEmpty) {
|
||||
return const Center(child: Text('チェックポイント履歴がありません'));
|
||||
}
|
||||
return ListView.builder(
|
||||
itemCount: historyController.checkpoints.length,
|
||||
itemBuilder: (context, index) {
|
||||
final checkpoint = historyController.checkpoints[index];
|
||||
return ListTile(
|
||||
title: Text('CP ${checkpoint.cp_number ?? 'Unknown'}'),
|
||||
subtitle: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text('通過時刻: ${_formatDateTime(checkpoint.checkintime)}'),
|
||||
Text('チーム: ${checkpoint.team_name ?? 'Unknown'}'),
|
||||
Text('イベント: ${checkpoint.event_code ?? 'Unknown'}'),
|
||||
],
|
||||
),
|
||||
leading: checkpoint.image != null
|
||||
? Image.file(
|
||||
File(checkpoint.image!),
|
||||
width: 50,
|
||||
height: 50,
|
||||
fit: BoxFit.cover,
|
||||
errorBuilder: (context, error, stackTrace) {
|
||||
print('Error loading image: $error');
|
||||
return const Icon(Icons.error);
|
||||
},
|
||||
)
|
||||
: const Icon(Icons.image_not_supported),
|
||||
);
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
String _formatDateTime(int? microsecondsSinceEpoch) {
|
||||
if (microsecondsSinceEpoch == null) return 'Unknown';
|
||||
final dateTime = DateTime.fromMicrosecondsSinceEpoch(microsecondsSinceEpoch);
|
||||
return DateFormat('yyyy-MM-dd HH:mm:ss').format(dateTime);
|
||||
}
|
||||
22
lib/pages/gps/gps_controller.dart
Normal file
22
lib/pages/gps/gps_controller.dart
Normal file
@ -0,0 +1,22 @@
|
||||
import 'package:get/get.dart';
|
||||
import 'package:gifunavi/model/gps_data.dart';
|
||||
import 'package:gifunavi/utils/database_gps.dart';
|
||||
import 'package:gifunavi/pages/index/index_controller.dart';
|
||||
|
||||
class GpsController extends GetxController {
|
||||
final gpsData = <GpsData>[].obs;
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
loadGpsData();
|
||||
}
|
||||
|
||||
void loadGpsData() async {
|
||||
final teamName = Get.find<IndexController>().currentUser[0]["user"]['team_name'];
|
||||
final eventCode = Get.find<IndexController>().currentUser[0]["user"]["event_code"];
|
||||
GpsDatabaseHelper db = GpsDatabaseHelper.instance;
|
||||
var data = await db.getGPSData(teamName, eventCode);
|
||||
gpsData.value = data;
|
||||
}
|
||||
}
|
||||
22
lib/pages/history/history_controller.dart
Normal file
22
lib/pages/history/history_controller.dart
Normal file
@ -0,0 +1,22 @@
|
||||
import 'dart:io';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:gifunavi/model/rog.dart';
|
||||
import 'package:gifunavi/utils/database_helper.dart';
|
||||
|
||||
class HistoryController extends GetxController {
|
||||
final checkpoints = <Rog>[].obs; // Rog オブジェクトのリストに変更
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
loadCheckpoints();
|
||||
}
|
||||
|
||||
void loadCheckpoints() async {
|
||||
DatabaseHelper db = DatabaseHelper.instance;
|
||||
var data = await db.allRogianing();
|
||||
checkpoints.value = data;
|
||||
}
|
||||
}
|
||||
@ -27,7 +27,7 @@ class _HistoryPageState extends State<HistoryPage> {
|
||||
future: db.getDestinations(),
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.connectionState == ConnectionState.waiting) {
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
return const Center(child: CircularProgressIndicator()); //History page
|
||||
} else if (snapshot.hasError) {
|
||||
return Center(child: Text('Error: ${snapshot.error}'));
|
||||
} else if (!snapshot.hasData || snapshot.data!.isEmpty) {
|
||||
@ -167,7 +167,7 @@ class CustomWidget extends StatelessWidget {
|
||||
print('Error loading image path: ${snapshot.error}');
|
||||
return const Icon(Icons.error);
|
||||
} else {
|
||||
return const CircularProgressIndicator();
|
||||
return const CircularProgressIndicator(); // History page 2
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
import 'package:connectivity_plus/connectivity_plus.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
@ -8,7 +9,6 @@ import 'package:flutter_map/flutter_map.dart';
|
||||
import 'package:flutter_polyline_points/flutter_polyline_points.dart';
|
||||
import 'package:geojson_vi/geojson_vi.dart';
|
||||
import 'package:geolocator/geolocator.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:latlong2/latlong.dart';
|
||||
import 'package:gifunavi/model/destination.dart';
|
||||
import 'package:gifunavi/model/entry.dart';
|
||||
@ -34,6 +34,8 @@ import 'package:timezone/data/latest.dart' as tz;
|
||||
|
||||
import '../permission/permission.dart';
|
||||
|
||||
//enum MapState { initializing, ready, error }
|
||||
|
||||
class IndexController extends GetxController with WidgetsBindingObserver {
|
||||
List<GeoJSONFeatureCollection> locations = <GeoJSONFeatureCollection>[].obs;
|
||||
List<GeoJSONFeature> currentFeature = <GeoJSONFeature>[].obs;
|
||||
@ -60,10 +62,16 @@ class IndexController extends GetxController with WidgetsBindingObserver {
|
||||
|
||||
var isCustomAreaSelected = false.obs;
|
||||
|
||||
RxBool isMapControllerReady = RxBool(false); // MapControllerの初期化状態を管理するフラグ
|
||||
//final mapControllerReadyStream = Stream<bool>.value(false); // MapControllerの初期化状態を通知するためのストリーム
|
||||
|
||||
MapController mapController = MapController();
|
||||
// 複数の状態変数(_isLoading, _isMapInitialized, indexController.isMapControllerReady)を一元管理します。
|
||||
//final Rx<MapState> mapState = MapState.initializing.obs;
|
||||
|
||||
final Completer<void> mapControllerCompleter = Completer<void>();
|
||||
late MapController mapController; // = Rx<MapController?>(null);
|
||||
final RxBool isMapControllerReady = false.obs; // MapControllerの初期化状態を管理するフラグ
|
||||
|
||||
//MapController mapController = MapController();
|
||||
MapController rogMapController = MapController();
|
||||
|
||||
LogManager logManager = LogManager();
|
||||
@ -96,7 +104,43 @@ class IndexController extends GetxController with WidgetsBindingObserver {
|
||||
|
||||
final selectedEventName = 'add_location'.tr.obs;
|
||||
|
||||
RxBool isLoadingLocations = true.obs;
|
||||
RxBool isLoadingLocations = false.obs;
|
||||
|
||||
// ユーザーの参加状況を追跡する
|
||||
final RxBool isUserParticipating = false.obs;
|
||||
|
||||
// ユーザーのゴール到達状況を追跡する
|
||||
final RxBool hasUserReachedGoal = false.obs;
|
||||
|
||||
void initMapController() {
|
||||
mapController = MapController();
|
||||
|
||||
// MapEventMoveEndイベントを使用して初期化完了を検出
|
||||
mapController.mapEventStream.listen((event) {
|
||||
if (event is MapEventMoveEnd && !isMapControllerReady.value) {
|
||||
// MapEventMoveEndイベントが発生したら、MapControllerの準備完了とみなす
|
||||
if (!isMapControllerReady.value) {
|
||||
print('MapController is ready');
|
||||
isMapControllerReady.value = true;
|
||||
if (!mapControllerCompleter.isCompleted) {
|
||||
mapControllerCompleter.complete();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// ユーザーの参加状況を更新するメソッド
|
||||
void updateUserParticipationStatus(bool isParticipating) {
|
||||
isUserParticipating.value = isParticipating;
|
||||
update(); // GetX の update() メソッドを呼び出してUIを更新
|
||||
}
|
||||
|
||||
// ユーザーのゴール到達状況を更新するメソッド
|
||||
void updateUserGoalStatus(bool hasReachedGoal) {
|
||||
hasUserReachedGoal.value = hasReachedGoal;
|
||||
update(); // GetX の update() メソッドを呼び出してUIを更新
|
||||
}
|
||||
|
||||
void setSelectedEventName(String eventName) {
|
||||
selectedEventName.value = eventName;
|
||||
@ -242,13 +286,14 @@ class IndexController extends GetxController with WidgetsBindingObserver {
|
||||
void onInit() {
|
||||
try {
|
||||
super.onInit();
|
||||
//mapController = MapController();
|
||||
//ever(isMapControllerReady, (_) => _onMapControllerReady());
|
||||
|
||||
initMapController();
|
||||
|
||||
initConnectivity();
|
||||
_connectivitySubscription = _connectivity.onConnectivityChanged.listen(_updateConnectionStatus);
|
||||
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
||||
await PermissionController.checkAndRequestPermissions();
|
||||
});
|
||||
|
||||
WidgetsBinding.instance.addObserver(this);
|
||||
_startLocationService(); // アプリ起動時にLocationServiceを開始する
|
||||
|
||||
@ -259,7 +304,7 @@ class IndexController extends GetxController with WidgetsBindingObserver {
|
||||
tz.initializeTimeZones();
|
||||
//teamController = Get.find<TeamController>();
|
||||
|
||||
loadLocations();
|
||||
//loadLocations();
|
||||
|
||||
}catch(e,stacktrace){
|
||||
print('Error in IndexController.onInit: $e');
|
||||
@ -270,18 +315,33 @@ class IndexController extends GetxController with WidgetsBindingObserver {
|
||||
}
|
||||
}
|
||||
|
||||
void _onMapControllerReady() {
|
||||
if (isMapControllerReady.value) {
|
||||
loadLocations();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> loadLocations() async {
|
||||
if (isLoadingLocations.value) return;
|
||||
|
||||
isLoadingLocations.value = true;
|
||||
try {
|
||||
await waitForMapControllerReady();
|
||||
debugPrint('IndexController: Starting to load locations');
|
||||
|
||||
await waitForMapControllerReady(); // loadLocations
|
||||
debugPrint('IndexController: Permission granted, loading locations');
|
||||
String eventCode = currentUser.isNotEmpty ? currentUser[0]["user"]["event_code"] ?? "" : "";
|
||||
await loadLocationsBound(eventCode);
|
||||
|
||||
debugPrint('IndexController: Locations loaded successfully');
|
||||
} catch (e) {
|
||||
print('Error loading locations: $e');
|
||||
// エラーハンドリングを追加(例:スナックバーでユーザーに通知)
|
||||
Get.snackbar('エラー', '位置情報の取得に失敗しました: ${e.toString()}');
|
||||
} finally {
|
||||
isLoadingLocations.value = false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void _updateConnectionStatus(List<ConnectivityResult> results) {
|
||||
@ -497,17 +557,20 @@ class IndexController extends GetxController with WidgetsBindingObserver {
|
||||
//
|
||||
Future<void> login(String email, String password) async {
|
||||
try {
|
||||
isLoading.value = true;
|
||||
final value = await AuthService.login(email, password);
|
||||
if (value.isNotEmpty && value['token'] != null) {
|
||||
await changeUser(value);
|
||||
await _initializeUserData();
|
||||
Get.offAllNamed(AppPages.INDEX);
|
||||
} else {
|
||||
Get.snackbar('Login Failed', 'Invalid credentials');
|
||||
Get.snackbar('ログイン失敗', 'メールアドレスまたはパスワードが間違っています');
|
||||
}
|
||||
} catch (e) {
|
||||
print('Login error: $e');
|
||||
Get.snackbar('Login Failed', 'An error occurred. Please try again.');
|
||||
Get.snackbar('ログイン失敗', 'エラーが発生しました。もう一度お試しください。');
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -897,27 +960,28 @@ class IndexController extends GetxController with WidgetsBindingObserver {
|
||||
return;
|
||||
}
|
||||
|
||||
// MapControllerの初期化が完了するまで待機
|
||||
await waitForMapControllerReady();
|
||||
try {
|
||||
// MapControllerの初期化が完了するまで待機
|
||||
await waitForMapControllerReady();
|
||||
|
||||
// null チェックを追加
|
||||
if (mapController.bounds == null) {
|
||||
print("MapController bounds are null");
|
||||
return;
|
||||
}
|
||||
// null チェックを追加
|
||||
if (mapController.bounds == null) {
|
||||
print("MapController bounds are null");
|
||||
return;
|
||||
}
|
||||
|
||||
// バウンドが有効かどうかを確認する
|
||||
LatLngBounds bounds = mapController.bounds!;
|
||||
if (!_isValidBounds(bounds)) {
|
||||
print("MapController bounds are not valid");
|
||||
return;
|
||||
}
|
||||
// バウンドが有効かどうかを確認する
|
||||
LatLngBounds bounds = mapController.bounds!;
|
||||
if (!_isValidBounds(bounds)) {
|
||||
print("MapController bounds are not valid");
|
||||
return;
|
||||
}
|
||||
|
||||
locations.clear();
|
||||
String cat = currentCat.isNotEmpty ? currentCat[0] : "";
|
||||
if (currentCat.isNotEmpty && currentCat[0] == "-all-") {
|
||||
cat = "";
|
||||
}
|
||||
locations.clear();
|
||||
String cat = currentCat.isNotEmpty ? currentCat[0] : "";
|
||||
if (currentCat.isNotEmpty && currentCat[0] == "-all-") {
|
||||
cat = "";
|
||||
}
|
||||
/*
|
||||
// Akira Add 2024-4-6
|
||||
if( mapController.controller == null ) {
|
||||
@ -928,13 +992,13 @@ class IndexController extends GetxController with WidgetsBindingObserver {
|
||||
//
|
||||
*/
|
||||
|
||||
//LatLngBounds bounds = mapController.bounds!;
|
||||
//LatLngBounds bounds = mapController.bounds!;
|
||||
|
||||
currentBound.clear();
|
||||
currentBound.add(bounds);
|
||||
|
||||
//isLoading.value = true; // ローディング状態をtrueに設定
|
||||
|
||||
currentBound.clear();
|
||||
currentBound.add(bounds);
|
||||
|
||||
//isLoading.value = true; // ローディング状態をtrueに設定
|
||||
|
||||
//print("bounds --- (${bounds.southWest.latitude},${bounds.southWest.longitude}),(${bounds.northWest.latitude},${bounds.northWest.longitude}),(${bounds.northEast.latitude},${bounds.northEast.longitude}),(${bounds.southEast.latitude},${bounds.southEast.longitude})");
|
||||
|
||||
// 要検討:APIからのレスポンスがnullの場合のエラーハンドリングが不十分です。適切なエラーメッセージを表示するなどの処理を追加してください。
|
||||
@ -950,7 +1014,7 @@ class IndexController extends GetxController with WidgetsBindingObserver {
|
||||
bounds.southEast.latitude,
|
||||
bounds.southEast.longitude,
|
||||
cat,
|
||||
eventCode
|
||||
eventCode
|
||||
);
|
||||
/*
|
||||
if (value == null) {
|
||||
@ -979,7 +1043,8 @@ class IndexController extends GetxController with WidgetsBindingObserver {
|
||||
"please zoom in",
|
||||
backgroundColor: Colors.yellow,
|
||||
colorText: Colors.white,
|
||||
icon: const Icon(Icons.assistant_photo_outlined, size: 40.0, color: Colors.blue),
|
||||
icon: const Icon(Icons.assistant_photo_outlined, size: 40.0,
|
||||
color: Colors.blue),
|
||||
snackPosition: SnackPosition.TOP,
|
||||
duration: const Duration(seconds: 3),
|
||||
);
|
||||
@ -1011,12 +1076,15 @@ class IndexController extends GetxController with WidgetsBindingObserver {
|
||||
locations.add(value);
|
||||
}
|
||||
*/
|
||||
} catch ( e) {
|
||||
} catch (e) {
|
||||
print("Error in loadLocationsBound: $e");
|
||||
// エラーが発生した場合のリトライ処理や適切なエラーメッセージの表示を行う
|
||||
// 例えば、一定時間後に再度loadLocationsBound()を呼び出すなど
|
||||
}
|
||||
|
||||
} catch (e) {
|
||||
print('Error in loadLocationsBound: $e');
|
||||
//Get.snackbar('エラー', '位置情報の読み込みに失敗しました。もう一度お試しください。');
|
||||
}
|
||||
}
|
||||
|
||||
// バウンドが有効かどうかを確認するヘルパーメソッド
|
||||
@ -1029,20 +1097,44 @@ class IndexController extends GetxController with WidgetsBindingObserver {
|
||||
bounds.southWest.latitude < bounds.northEast.latitude;
|
||||
}
|
||||
|
||||
static const int maxRetries = 5;
|
||||
int retryCount = 0;
|
||||
|
||||
//===Akira 追加:2024-4-6 #2800
|
||||
// 要検討:MapControllerの初期化が完了するまで待機していますが、タイムアウトを設定することを検討してください。
|
||||
// 初期化に時間がかかりすぎる場合、ユーザーにわかりやすいメッセージを表示するようにしてください。
|
||||
//
|
||||
Future<void> waitForMapControllerReady() async {
|
||||
if (!isMapControllerReady.value) {
|
||||
await Future.doWhile(() async {
|
||||
await Future.delayed(const Duration(milliseconds: 100));
|
||||
return !isMapControllerReady.value;
|
||||
});
|
||||
if (isMapControllerReady.value) return;
|
||||
|
||||
//await isMapControllerReady.firstWhere((value) => value == true);
|
||||
|
||||
const timeout = Duration(seconds: 30);
|
||||
try {
|
||||
await Future.any([
|
||||
mapControllerCompleter.future,
|
||||
Future.delayed(timeout).then((_) => throw TimeoutException('MapController initialization timed out')),
|
||||
]);
|
||||
isMapControllerReady.value = true;
|
||||
} catch (e) {
|
||||
print('Warning waiting for MapController: $e');
|
||||
// タイムアウト後もマップが機能している場合は、強制的に準備完了とマーク
|
||||
if (!isMapControllerReady.value) {
|
||||
isMapControllerReady.value = true;
|
||||
if (!mapControllerCompleter.isCompleted) {
|
||||
mapControllerCompleter.complete();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//===Akira 追加:2024-4-6 #2800
|
||||
|
||||
void retryMapInitialization() {
|
||||
// マップの再初期化ロジック
|
||||
initMapController();
|
||||
// 他の必要な再初期化処理
|
||||
}
|
||||
|
||||
void setBound(LatLngBounds bounds) {
|
||||
currentBound.clear();
|
||||
currentBound.add(bounds);
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:geolocator/geolocator.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:gifunavi/pages/destination/destination_controller.dart';
|
||||
import 'package:gifunavi/pages/drawer/drawer_page.dart';
|
||||
@ -34,22 +35,45 @@ class IndexPage extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _IndexPageState extends State<IndexPage> {
|
||||
final IndexController indexController = Get.find<IndexController>();
|
||||
final DestinationController destinationController = Get.find<DestinationController>();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
||||
await _ensureControllersAreInitialized();
|
||||
await PermissionController.checkAndRequestPermissions();
|
||||
});
|
||||
_checkPermissionAndInitialize();
|
||||
}
|
||||
|
||||
Future<void> _ensureControllersAreInitialized() async {
|
||||
while (!Get.isRegistered<IndexController>() ||
|
||||
!Get.isRegistered<DestinationController>() ||
|
||||
!Get.isRegistered<LocationController>()) {
|
||||
await Future.delayed(const Duration(milliseconds: 100));
|
||||
}
|
||||
Future<void> _checkPermissionAndInitialize() async {
|
||||
// 位置情報の許可が得られた場合の処理
|
||||
await _initializeMap();
|
||||
}
|
||||
|
||||
Future<void> _initializeMap() async {
|
||||
await indexController.loadLocations();
|
||||
setState(() {}); // 状態を更新してUIを再構築
|
||||
}
|
||||
|
||||
void _showLocationServiceDisabledOrDeniedError() {
|
||||
Get.snackbar(
|
||||
'エラー',
|
||||
'位置情報サービスが無効か、許可されていません。設定を確認してください。',
|
||||
duration: const Duration(seconds: 5),
|
||||
snackPosition: SnackPosition.BOTTOM,
|
||||
mainButton: TextButton(
|
||||
child: const Text('設定'),
|
||||
onPressed: () => Geolocator.openLocationSettings(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _showPermissionDeniedError() {
|
||||
Get.snackbar(
|
||||
'エラー',
|
||||
'位置情報の許可が必要です。設定から許可してください。',
|
||||
duration: const Duration(seconds: 5),
|
||||
snackPosition: SnackPosition.BOTTOM,
|
||||
);
|
||||
}
|
||||
|
||||
void checkEventAndNavigate() async {
|
||||
@ -112,11 +136,11 @@ class _IndexPageState extends State<IndexPage> {
|
||||
void _showEventSelectionWarning() {
|
||||
Get.dialog(
|
||||
AlertDialog(
|
||||
title: Text('警告'),
|
||||
content: Text('イベントを選択してください。'),
|
||||
title: const Text('警告'),
|
||||
content: const Text('イベントを選択してください。'),
|
||||
actions: [
|
||||
TextButton(
|
||||
child: Text('OK'),
|
||||
child: const Text('OK'),
|
||||
onPressed: () => Get.back(),
|
||||
),
|
||||
],
|
||||
@ -124,24 +148,27 @@ class _IndexPageState extends State<IndexPage> {
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
// class IndexPage extends GetView<IndexController> {
|
||||
// IndexPage({Key? key}) : super(key: key);
|
||||
|
||||
// IndexControllerとDestinationControllerのインスタンスを取得しています。
|
||||
//
|
||||
final LocationController locationController = Get.find<LocationController>();
|
||||
final IndexController indexController = Get.find<IndexController>();
|
||||
final DestinationController destinationController =
|
||||
Get.find<DestinationController>();
|
||||
|
||||
// buildメソッドは、ウィジェットのUIを構築するメソッドです。
|
||||
// ここでは、WillPopScopeウィジェットを使用して、端末の戻るボタンが押された際の動作を制御しています。
|
||||
//
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
||||
/*
|
||||
return PopScope(
|
||||
canPop: false,
|
||||
child: Scaffold(
|
||||
|
||||
*/
|
||||
return Scaffold(
|
||||
//
|
||||
// Scaffoldウィジェットを使用して、アプリのメインページのレイアウトを構築しています。
|
||||
//
|
||||
@ -150,24 +177,8 @@ class _IndexPageState extends State<IndexPage> {
|
||||
title: Obx(() => Text(indexController.selectedEventName.value)),
|
||||
//title: Text("add_location".tr),
|
||||
actions: [
|
||||
// IconButton(
|
||||
// onPressed: () {
|
||||
// DatabaseService ds = DatabaseService();
|
||||
// ds.updateDatabase();
|
||||
// },
|
||||
// icon: const Icon(Icons.ten_k_sharp)),
|
||||
|
||||
//
|
||||
// AppBarには、タイトルとアクションアイコンが含まれています。
|
||||
// アクションアイコンには、GPSデータの表示、履歴の表示、マップの更新、検索などの機能が含まれています。
|
||||
//
|
||||
IconButton(
|
||||
onPressed: () async {
|
||||
// GpsDatabaseHelper db = GpsDatabaseHelper.instance;
|
||||
// List<GpsData> data = await db.getGPSData(
|
||||
// indexController.currentUser[0]["user"]['team_name'],
|
||||
// indexController.currentUser[0]["user"]["event_code"]);
|
||||
// print("GPS data is ${data.length}");
|
||||
Get.toNamed(AppPages.GPS);
|
||||
},
|
||||
icon: const Icon(Icons.telegram)),
|
||||
@ -201,102 +212,22 @@ class _IndexPageState extends State<IndexPage> {
|
||||
),
|
||||
),
|
||||
//CatWidget(indexController: indexController,),
|
||||
//
|
||||
// デバッグ時のみリロードボタンの横にGPS信号レベルの設定ボタンを設置し、
|
||||
// タップすることでGPS信号の強弱をシミュレーションできるようにする
|
||||
// Akira 2024-4-5
|
||||
//
|
||||
/*
|
||||
Obx(() {
|
||||
if (locationController.isSimulationMode) {
|
||||
return DropdownButton<String>(
|
||||
value: locationController.getSimulatedSignalStrength(),
|
||||
onChanged: (value) {
|
||||
//debugPrint("DropDown changed!");
|
||||
locationController.setSimulatedSignalStrength(value!);
|
||||
},
|
||||
items: ['low', 'medium', 'high', 'real']
|
||||
.map<DropdownMenuItem<String>>((String value) {
|
||||
return DropdownMenuItem<String>(
|
||||
value: value,
|
||||
child: Text(value),
|
||||
);
|
||||
}).toList(),
|
||||
);
|
||||
} else {
|
||||
return Container();
|
||||
}
|
||||
}),
|
||||
*/
|
||||
|
||||
],
|
||||
),
|
||||
// bottomNavigationBar: BottomAppBar(
|
||||
// child: Row(
|
||||
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
// children: <Widget>[
|
||||
// Obx(
|
||||
// () => destinationController.isInRog.value == true
|
||||
// ? IconButton(
|
||||
// onPressed: () {},
|
||||
// icon: const Icon(
|
||||
// Icons.run_circle,
|
||||
// size: 44,
|
||||
// color: Colors.green,
|
||||
// ))
|
||||
// : IconButton(
|
||||
// onPressed: () {},
|
||||
// icon: const Icon(
|
||||
// Icons.run_circle,
|
||||
// size: 44,
|
||||
// color: Colors.black12,
|
||||
// )),
|
||||
// ),
|
||||
// Padding(
|
||||
// padding:
|
||||
// const EdgeInsets.only(right: 10.0, top: 4.0, bottom: 4.0),
|
||||
// child: InkWell(
|
||||
// child: Obx(() => destinationController
|
||||
// .isGpsSelected.value ==
|
||||
// true
|
||||
// ? Padding(
|
||||
// padding: const EdgeInsets.only(
|
||||
// right: 10.0, top: 4.0, bottom: 4.0),
|
||||
// child: InkWell(
|
||||
// child: const Image(
|
||||
// image:
|
||||
// AssetImage('assets/images/route3_off.png'),
|
||||
// width: 35,
|
||||
// height: 35,
|
||||
// ),
|
||||
// onTap: () {
|
||||
// //indexController.switchPage(AppPages.TRAVEL);
|
||||
// },
|
||||
// ),
|
||||
// )
|
||||
// : Padding(
|
||||
// padding: const EdgeInsets.only(
|
||||
// right: 10.0, top: 4.0, bottom: 4.0),
|
||||
// child: InkWell(
|
||||
// child: const Image(
|
||||
// image:
|
||||
// AssetImage('assets/images/route2_on.png'),
|
||||
// width: 35,
|
||||
// height: 35,
|
||||
// ),
|
||||
// onTap: () {
|
||||
// //indexController.switchPage(AppPages.TRAVEL);
|
||||
// },
|
||||
// ),
|
||||
// ))),
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
|
||||
//
|
||||
body: SafeArea(
|
||||
child:Obx(() {
|
||||
if (indexController.isLoadingLocations.value) {
|
||||
return const Center(child: CircularProgressIndicator()); // Index Controller
|
||||
} else {
|
||||
return indexController.mode.value == 0
|
||||
? const MapWidget()
|
||||
: const ListWidget();
|
||||
}
|
||||
}),
|
||||
),
|
||||
// マップモードとリストモードを切り替えるためのボタンです。
|
||||
//
|
||||
floatingActionButton: FloatingActionButton(
|
||||
onPressed: () {
|
||||
indexController.toggleMode();
|
||||
@ -312,22 +243,6 @@ class _IndexPageState extends State<IndexPage> {
|
||||
),
|
||||
),
|
||||
floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
|
||||
//
|
||||
// bodyには、SafeAreaウィジェットを使用して、画面の安全な領域内にUIを構築しています。
|
||||
//
|
||||
body: SafeArea(
|
||||
child: Column(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Obx(
|
||||
() => indexController.mode.value == 0
|
||||
? const MapWidget()
|
||||
: const ListWidget(),
|
||||
))
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -182,7 +182,7 @@ class _LoginPageState extends State<LoginPage> {
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius:
|
||||
BorderRadius.circular(40)),
|
||||
child: const CircularProgressIndicator(),
|
||||
child: const CircularProgressIndicator(), // Login page
|
||||
)
|
||||
: Column(
|
||||
children: [
|
||||
@ -199,24 +199,19 @@ class _LoginPageState extends State<LoginPage> {
|
||||
.tr,
|
||||
backgroundColor: Colors.red,
|
||||
colorText: Colors.white,
|
||||
icon: const Icon(
|
||||
Icons
|
||||
.assistant_photo_outlined,
|
||||
size: 40.0,
|
||||
color: Colors.blue),
|
||||
snackPosition:
|
||||
SnackPosition.TOP,
|
||||
duration: const Duration(
|
||||
seconds: 3),
|
||||
icon: const Icon(Icons.assistant_photo_outlined, size: 40.0, color: Colors.blue),
|
||||
snackPosition: SnackPosition.TOP,
|
||||
duration: const Duration(seconds: 3),
|
||||
);
|
||||
return;
|
||||
}
|
||||
indexController.isLoading.value =
|
||||
true;
|
||||
indexController.login(
|
||||
emailController.text,
|
||||
passwordController.text
|
||||
);
|
||||
try {
|
||||
//indexController.isLoading.value = true;
|
||||
indexController.login(emailController.text, passwordController.text);
|
||||
} catch (e) {
|
||||
print('Error during login: $e');
|
||||
// エラーハンドリングは IndexController 内で行われるため、ここでは何もしない
|
||||
}
|
||||
},
|
||||
color: Colors.indigoAccent[400],
|
||||
shape: RoundedRectangleBorder(
|
||||
|
||||
@ -2,23 +2,87 @@ import 'dart:io';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:geolocator/geolocator.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:permission_handler/permission_handler.dart';
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
|
||||
class PermissionController {
|
||||
static bool? _locationPermissionGranted;
|
||||
|
||||
static bool _isRequestingPermission = false;
|
||||
static Completer<bool>? _permissionCompleter;
|
||||
/*
|
||||
bool? _isRequestingPermission=false;
|
||||
final Completer<PermissionController> _permissionCompleter = Completer<PermissionController>();
|
||||
*/
|
||||
|
||||
static Future<bool> checkAndRequestPermissions() async {
|
||||
if (_isRequestingPermission) {
|
||||
return _permissionCompleter!.future;
|
||||
static Future<bool> checkAndRequestPermissions_new() async {
|
||||
try {
|
||||
if (_locationPermissionGranted != null) {
|
||||
return _locationPermissionGranted!;
|
||||
}
|
||||
|
||||
var serviceEnabled = await Geolocator.isLocationServiceEnabled();
|
||||
if (!serviceEnabled) {
|
||||
Get.snackbar(
|
||||
'エラー', '位置情報サービスが無効です。設定から有効にしてください。');
|
||||
|
||||
// 位置情報サービスが無効の場合、ユーザーに有効化を促す
|
||||
return false;
|
||||
}
|
||||
|
||||
debugPrint("====> Geolocator.checkPermission");
|
||||
var permission = await Geolocator.checkPermission();
|
||||
if (permission == LocationPermission.denied) {
|
||||
permission = await Geolocator.requestPermission();
|
||||
if (permission == LocationPermission.denied) {
|
||||
Get.snackbar('エラー', '位置情報の許可が拒否されました。');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (permission == LocationPermission.deniedForever) {
|
||||
Get.snackbar('エラー',
|
||||
'位置情報の許可が永久に拒否されました。設定から許可してください。');
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
_locationPermissionGranted = true;
|
||||
return true;
|
||||
} catch (e) {
|
||||
print('Error checking permissions: $e');
|
||||
Get.snackbar('エラー', '権限の確認中にエラーが発生しました。');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static void resetPermissionCache() {
|
||||
_locationPermissionGranted = null;
|
||||
}
|
||||
|
||||
static Future<bool> _requestLocationPermission() async {
|
||||
BuildContext? context;
|
||||
int attempts = 0;
|
||||
const maxAttempts = 10;
|
||||
|
||||
debugPrint("====> _requestLocationPermission");
|
||||
|
||||
// コンテキストが利用可能になるまで待機するロジック
|
||||
while (Get.context == null && attempts < maxAttempts) {
|
||||
context = Get.context;
|
||||
if (context == null) {
|
||||
await Future.delayed(const Duration(milliseconds: 500));
|
||||
attempts++;
|
||||
}
|
||||
}
|
||||
|
||||
_isRequestingPermission = true;
|
||||
_permissionCompleter = Completer<bool>();
|
||||
if (Get.context == null) {
|
||||
print('Context is still null after waiting, cannot proceed with permission check');
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
bool hasPermissions = await _checkLocationPermissions();
|
||||
@ -36,18 +100,16 @@ class PermissionController {
|
||||
SystemNavigator.pop();
|
||||
}
|
||||
}
|
||||
return hasPermissions;
|
||||
|
||||
_isRequestingPermission = false;
|
||||
_permissionCompleter!.complete(hasPermissions);
|
||||
} catch (e) {
|
||||
print('Error in permission request: $e');
|
||||
_isRequestingPermission = false;
|
||||
_permissionCompleter!.complete(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
return _permissionCompleter!.future;
|
||||
}
|
||||
|
||||
|
||||
static Future<bool> _checkLocationPermissions() async {
|
||||
final locationPermission = await Permission.location.status;
|
||||
final whenInUsePermission = await Permission.locationWhenInUse.status;
|
||||
@ -118,20 +180,30 @@ class PermissionController {
|
||||
(whenInUsePermission == PermissionStatus.granted || alwaysPermission == PermissionStatus.granted);
|
||||
}
|
||||
|
||||
static Future<bool> checkAndRequestPermissions_old() async {
|
||||
static Future<bool> checkAndRequestPermissions() async {
|
||||
|
||||
/*
|
||||
if (_isRequestingPermission) {
|
||||
return _permissionCompleter!.future;
|
||||
}
|
||||
|
||||
_isRequestingPermission = true;
|
||||
_permissionCompleter = Completer<bool>();
|
||||
*/
|
||||
|
||||
try {
|
||||
if (_locationPermissionGranted != null) {
|
||||
return _locationPermissionGranted!;
|
||||
}
|
||||
|
||||
bool hasPermissions = await _checkLocationPermissions();
|
||||
if (!hasPermissions) {
|
||||
bool userAgreed = await showLocationDisclosure();
|
||||
if (userAgreed) {
|
||||
hasPermissions = await _requestAllLocationPermissions();
|
||||
_locationPermissionGranted = true;
|
||||
debugPrint("Finish checkAndRequestPermissions...");
|
||||
return true;
|
||||
} else {
|
||||
print('User did not agree to location usage');
|
||||
hasPermissions = false;
|
||||
@ -140,15 +212,18 @@ class PermissionController {
|
||||
}
|
||||
}
|
||||
|
||||
_isRequestingPermission = false;
|
||||
_permissionCompleter!.complete(hasPermissions);
|
||||
//_isRequestingPermission = false;
|
||||
//_permissionCompleter!.complete(hasPermissions);
|
||||
|
||||
|
||||
} catch( e ) {
|
||||
print('Error in permission request: $e');
|
||||
_isRequestingPermission = false;
|
||||
_permissionCompleter!.complete(false);
|
||||
//_isRequestingPermission = false;
|
||||
//_permissionCompleter!.complete(false);
|
||||
}
|
||||
debugPrint("Finish checkAndRequestPermissions...");
|
||||
return _permissionCompleter!.future;
|
||||
//return _permissionCompleter!.future;
|
||||
return false;
|
||||
}
|
||||
|
||||
static Future<void> requestAllLocationPermissions() async {
|
||||
@ -171,10 +246,10 @@ class PermissionController {
|
||||
print('Context is null, cannot show dialog');
|
||||
return false;
|
||||
}
|
||||
if (Get.isDialogOpen ?? false) {
|
||||
print('A dialog is already open');
|
||||
return false;
|
||||
}
|
||||
//if (Get.isDialogOpen ?? false) {
|
||||
// print('A dialog is already open');
|
||||
// return false;
|
||||
//}
|
||||
|
||||
try {
|
||||
final result = await Get.dialog<bool>(
|
||||
|
||||
@ -9,6 +9,12 @@ class SettingsController extends GetxController {
|
||||
var autoReturnDisabled = false.obs;
|
||||
final MapResetController mapResetController = Get.put(MapResetController());
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
ever(timerDuration, (_) => resetIdleTimer());
|
||||
}
|
||||
|
||||
void updateTimerDuration(int seconds) {
|
||||
timerDuration.value = Duration(seconds: seconds);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user