temporary update

This commit is contained in:
2024-09-08 18:16:51 +09:00
parent 2c0bb06e74
commit e37c4ceebd
32 changed files with 1235 additions and 1189 deletions

View File

@ -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);

View File

@ -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(),
))
],
),
),
),
);
);
}
}