CPラベルが1文字しか出ない、起動が遅い

This commit is contained in:
2024-04-14 20:16:13 +09:00
parent f6b2a6c7d4
commit 4ef42216f8
18 changed files with 1520 additions and 372 deletions

View File

@ -24,7 +24,7 @@ import 'package:rogapp/services/perfecture_service.dart';
import 'package:rogapp/utils/database_gps.dart'; import 'package:rogapp/utils/database_gps.dart';
import 'package:rogapp/utils/database_helper.dart'; import 'package:rogapp/utils/database_helper.dart';
import 'package:rogapp/utils/location_controller.dart'; import 'package:rogapp/utils/location_controller.dart';
import 'package:rogapp/widgets/bottom_sheet_new.dart'; import 'package:rogapp/widgets/bottom_sheets/bottom_sheet_normal_point.dart';
import 'dart:async'; import 'dart:async';
import 'package:modal_bottom_sheet/modal_bottom_sheet.dart'; import 'package:modal_bottom_sheet/modal_bottom_sheet.dart';
@ -33,14 +33,12 @@ import 'package:shared_preferences/shared_preferences.dart';
import 'package:image_gallery_saver/image_gallery_saver.dart'; import 'package:image_gallery_saver/image_gallery_saver.dart';
import 'package:geolocator/geolocator.dart';
// 目的地に関連する状態管理とロジックを担当するクラスです。 // 目的地に関連する状態管理とロジックを担当するクラスです。
// //
class DestinationController extends GetxController { class DestinationController extends GetxController {
late LocationSettings locationSettings; // 位置情報の設定を保持する変数です。 late LocationSettings locationSettings; // 位置情報の設定を保持する変数です。
Timer? _GPStimer; // GPSタイマーを保持する変数です。 //Timer? _GPStimer; // GPSタイマーを保持する変数です。
var destinationCount = 0.obs; // 目的地の数を保持するReactive変数です。 var destinationCount = 0.obs; // 目的地の数を保持するReactive変数です。
List<Destination> destinations = <Destination>[].obs; // 目的地のリストを保持するObservable変数です。 List<Destination> destinations = <Destination>[].obs; // 目的地のリストを保持するObservable変数です。
@ -92,7 +90,20 @@ class DestinationController extends GetxController {
int _start = 0; // 開始時刻を保持する変数です。 int _start = 0; // 開始時刻を保持する変数です。
int chekcs = 0; // チェックポイントの数を保持する変数です。 int chekcs = 0; // チェックポイントの数を保持する変数です。
var rogainingCounted = false.obs; // ロゲイニングがカウントされたかどうかを示すReactive変数です。 var rogainingCounted = false.obs; // ロゲイニングがカウントされたかどうかを示すReactive変数です。
// destinationController.rogainingCountedは、現在のロゲイニングセッションでポイントがカウントされたかどうかを管理するフラグです。
//
// このフラグは以下のような状況で使用されます:
//
// ロゲイニングを開始したとき、rogainingCountedはfalseに初期化されます。これは、まだポイントがカウントされていないことを示します。
// チェックポイントに到着し、チェックインが成功したとき、rogainingCountedはtrueに設定されます。これは、そのセッションでポイントがカウントされたことを示します。
// ロゲイニングを終了したとき、rogainingCountedは再びfalseに設定されます。これは、次のセッションに備えてフラグをリセットするためです。
// このフラグは、主に以下の目的で使用されます:
//
// ゴール地点でのロジックの制御rogainingCountedがtrueの場合、つまりポイントがカウントされている場合にのみ、ゴール処理を実行できます。
// UI の更新rogainingCountedの状態に基づいて、適切なメッセージやボタンを表示することができます。
/* /*
//==== Akira .. GPS信号シミュレーション用 ===== ここから、2024-4-5 //==== Akira .. GPS信号シミュレーション用 ===== ここから、2024-4-5
@ -317,15 +328,17 @@ class DestinationController extends GetxController {
isAtStart.value = true; isAtStart.value = true;
if (shouldShowBottomSheet) { if (shouldShowBottomSheet) {
shouldShowBottomSheet = false; shouldShowBottomSheet = false;
if (d.cp == -1) return;
if (d.cp == -1||d.cp==-2||d.cp==0) return;
Widget bottomSheet = BottomSheetNormalPoint(destination: d);
await showModalBottomSheet( await showModalBottomSheet(
constraints: constraints:
BoxConstraints.loose(Size(Get.width, Get.height * 0.85)), BoxConstraints.loose(Size(Get.width, Get.height * 0.85)),
context: Get.context!, context: Get.context!,
isScrollControlled: true, isScrollControlled: true,
builder: ((context) => BottomSheetNew( builder: ((context) => bottomSheet)
destination: d, ).whenComplete(() {
))).whenComplete(() {
shouldShowBottomSheet = true; shouldShowBottomSheet = true;
skipGps = false; skipGps = false;
chekcs = 0; chekcs = 0;
@ -342,15 +355,17 @@ class DestinationController extends GetxController {
isInCheckin.value = true; isInCheckin.value = true;
if (shouldShowBottomSheet) { if (shouldShowBottomSheet) {
shouldShowBottomSheet = false; shouldShowBottomSheet = false;
if (d.cp == -1) return;
if (d.cp == -1||d.cp==-2||d.cp==0) return;
Widget bottomSheet = BottomSheetNormalPoint(destination: d);
await showModalBottomSheet( await showModalBottomSheet(
constraints: constraints:
BoxConstraints.loose(Size(Get.width, Get.height * 0.75)), BoxConstraints.loose(Size(Get.width, Get.height * 0.75)),
context: Get.context!, context: Get.context!,
isScrollControlled: true, isScrollControlled: true,
builder: ((context) => BottomSheetNew( builder: ((context) => bottomSheet)
destination: d, ).whenComplete(() {
))).whenComplete(() {
shouldShowBottomSheet = true; shouldShowBottomSheet = true;
skipGps = false; skipGps = false;
chekcs = 0; chekcs = 0;
@ -412,14 +427,16 @@ class DestinationController extends GetxController {
isInCheckin.value = true; isInCheckin.value = true;
if (shouldShowBottomSheet) { if (shouldShowBottomSheet) {
shouldShowBottomSheet = false; shouldShowBottomSheet = false;
if (d.cp == -1) return;
if (d.cp == -1||d.cp==-2||d.cp==0) return;
Widget bottomSheet = BottomSheetNormalPoint(destination: d);
await showMaterialModalBottomSheet( await showMaterialModalBottomSheet(
expand: true, expand: true,
context: Get.context!, context: Get.context!,
backgroundColor: Colors.transparent, backgroundColor: Colors.transparent,
builder: (context) => BottomSheetNew( builder: (context) => bottomSheet
destination: d, ).whenComplete(() {
)).whenComplete(() {
shouldShowBottomSheet = true; shouldShowBottomSheet = true;
skipGps = false; skipGps = false;
chekcs = 0; chekcs = 0;
@ -506,15 +523,17 @@ class DestinationController extends GetxController {
isAtStart.value = true; isAtStart.value = true;
if (shouldShowBottomSheet) { if (shouldShowBottomSheet) {
shouldShowBottomSheet = false; shouldShowBottomSheet = false;
if (d.cp == -1) return;
if (d.cp == -1||d.cp==-2||d.cp==0) return;
Widget bottomSheet = BottomSheetNormalPoint(destination: d);
await showModalBottomSheet( await showModalBottomSheet(
constraints: constraints:
BoxConstraints.loose(Size(Get.width, Get.height * 0.75)), BoxConstraints.loose(Size(Get.width, Get.height * 0.75)),
context: Get.context!, context: Get.context!,
isScrollControlled: true, isScrollControlled: true,
builder: ((context) => BottomSheetNew( builder: ((context) => bottomSheet)
destination: d, ).whenComplete(() {
))).whenComplete(() {
shouldShowBottomSheet = true; shouldShowBottomSheet = true;
//print("----- finished start -------"); //print("----- finished start -------");
skipGps = false; skipGps = false;
@ -932,11 +951,11 @@ class DestinationController extends GetxController {
// //
locationController.locationMarkerPositionStream.listen( locationController.locationMarkerPositionStream.listen(
(locationMarkerPosition) { (locationMarkerPosition) {
if (locationMarkerPosition != null) { //if (locationMarkerPosition != null) {
handleLocationUpdate(locationMarkerPosition); handleLocationUpdate(locationMarkerPosition);
} //}
}, onError: (err) { }, onError: (err) {
print("Error: $err"); print("Location Error: $err");
}); });
startGame(); startGame();
@ -957,7 +976,7 @@ class DestinationController extends GetxController {
// //
void handleLocationUpdate(LocationMarkerPosition? position) async { void handleLocationUpdate(LocationMarkerPosition? position) async {
try { try {
final DestinationController destinationController = Get.find<DestinationController>(); //final DestinationController destinationController = Get.find<DestinationController>();
//final signalStrength = locationController.getGpsSignalStrength(); //final signalStrength = locationController.getGpsSignalStrength();
okToUseGPS = false; okToUseGPS = false;
@ -1209,10 +1228,7 @@ class DestinationController extends GetxController {
// 地図を現在位置に中央揃えする関数です。 // 地図を現在位置に中央揃えする関数です。
// //
void centerMapToCurrentLocation() { void centerMapToCurrentLocation() {
assert(() { //print("center is ${currentLat}, ${currentLon}");
print("center is ${currentLat}, ${currentLon}");
return true;
}());
// Akira ... 状況によって呼ぶか呼ばないか // Akira ... 状況によって呼ぶか呼ばないか
if (currentLat != 0 || currentLon != 0) { if (currentLat != 0 || currentLon != 0) {
indexController.mapController.move(LatLng(currentLat, currentLon), 17.0); indexController.mapController.move(LatLng(currentLat, currentLon), 17.0);

View File

@ -12,8 +12,24 @@ import 'package:rogapp/pages/destination/destination_controller.dart';
import 'package:rogapp/pages/index/index_controller.dart'; import 'package:rogapp/pages/index/index_controller.dart';
import 'package:rogapp/utils/text_util.dart'; import 'package:rogapp/utils/text_util.dart';
import 'package:rogapp/widgets/base_layer_widget.dart'; import 'package:rogapp/widgets/base_layer_widget.dart';
import 'package:rogapp/widgets/bottom_sheet_new.dart'; //import 'package:rogapp/widgets/bottom_sheet_new.dart';
import 'package:rogapp/widgets/bottom_sheets/bottom_sheet_start.dart';
import 'package:rogapp/widgets/bottom_sheets/bottom_sheet_goal.dart';
import 'package:rogapp/widgets/bottom_sheets/bottom_sheet_normal_point.dart';
// FlutterMapウィジェットを使用して、地図を表示します。
// IndexControllerから目的地のリストを取得し、マーカーとしてマップ上に表示します。
// マーカーがタップされると、BottomSheetウィジェットを表示します。
// 現在地の表示、ルートの表示、ベースレイヤーの表示などの機能を提供します。
// 主なロジック:
// FlutterMapウィジェットを使用して、地図を表示します。
// IndexControllerから目的地のリストを取得し、MarkerLayerを使用してマーカーを表示します。
// getMarkerShapeメソッドを使用して、マーカーの見た目をカスタマイズします。目的地の種類に応じて、異なるマーカーを表示します。
// マーカーがタップされると、festuretoDestinationメソッドを使用してGeoJSONFeatureをDestinationオブジェクトに変換し、showModalBottomSheetを使用してBottomSheetウィジェットを表示します。
// CurrentLocationLayerを使用して、現在地をマップ上に表示します。
// PolylineLayerを使用して、ルートをマップ上に表示します。getPointsメソッドを使用して、ルートの座標を取得します。
// BaseLayerを使用して、マップのベースレイヤーを表示します。
//
class DestinationMapPage extends StatelessWidget { class DestinationMapPage extends StatelessWidget {
DestinationMapPage({Key? key}) : super(key: key); DestinationMapPage({Key? key}) : super(key: key);
@ -54,14 +70,23 @@ class DestinationMapPage extends StatelessWidget {
indexController.currentDestinationFeature.add(d); indexController.currentDestinationFeature.add(d);
//indexController.getAction(); //indexController.getAction();
Widget bottomSheet;
if (d.cp == -1 || d.cp == 0) {
bottomSheet = BottomSheetStart(destination: d);
} else if (d.cp == -2 || d.cp == 0) {
bottomSheet = BottomSheetGoal(destination: d);
} else {
bottomSheet = BottomSheetNormalPoint(destination: d);
}
showModalBottomSheet( showModalBottomSheet(
context: Get.context!, context: Get.context!,
isScrollControlled: true, isScrollControlled: true,
constraints: constraints:
BoxConstraints.loose(Size(Get.width, Get.height * 0.85)), BoxConstraints.loose(Size(Get.width, Get.height * 0.85)),
builder: ((context) => BottomSheetNew( builder: ((context) => bottomSheet ),
destination: d,
))).whenComplete(() { ).whenComplete(() {
//print("---- set skip gps to false -----"); //print("---- set skip gps to false -----");
destinationController.skipGps = false; destinationController.skipGps = false;
}); });

View File

@ -408,11 +408,11 @@ class IndexController extends GetxController {
if (value == null) { if (value == null) {
// APIからのレスポンスがnullの場合 // APIからのレスポンスがnullの場合
print("LocationService.loadLocationsBound からの回答がnullのため、マップをリロード"); print("LocationService.loadLocationsBound からの回答がnullのため、マップをリロード");
//DestinationController destinationController = Get.find<DestinationController>(); // 追加 DestinationController destinationController = Get.find<DestinationController>(); // 追加
//final tk = currentUser[0]["token"]; // 追加 final tk = currentUser[0]["token"]; // 追加
//if (tk != null) { // 追加 if (tk != null) { // 追加
// destinationController.fixMapBound(tk); // 追加 destinationController.fixMapBound(tk); // 追加
//} // 追加 } // 追加
return; return;
} }
isLoading.value = false; // ローディング状態をfalseに設定 isLoading.value = false; // ローディング状態をfalseに設定

View File

@ -104,13 +104,39 @@ class IndexPage extends GetView<IndexController> {
// タップすることでGPS信号の強弱をシミュレーションできるようにする // タップすることでGPS信号の強弱をシミュレーションできるようにする
// Akira 2024-4-5 // 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 InkWell(
onTap: () {
locationController.setSimulatedSignalStrength('high');
},
child: const Icon(Icons.info),
);
}
}),
/*
Obx(() => locationController.isSimulationMode Obx(() => locationController.isSimulationMode
? DropdownButton<String>( ? DropdownButton<String>(
value: locationController.getSimulatedSignalStrength(), value: locationController.getSimulatedSignalStrength(),
onChanged: (value) { onChanged: (value) {
locationController.setSimulatedSignalStrength(value!); locationController.setSimulatedSignalStrength(value!);
}, },
items: ['high', 'medium', 'low'] items: ['low', 'medium', 'high','real']
.map<DropdownMenuItem<String>>((String value) { .map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>( return DropdownMenuItem<String>(
value: value, value: value,
@ -120,6 +146,7 @@ class IndexPage extends GetView<IndexController> {
) )
: Container(), : Container(),
), ),
*/
], ],
), ),
// bottomNavigationBar: BottomAppBar( // bottomNavigationBar: BottomAppBar(

View File

@ -6,7 +6,9 @@ import 'package:rogapp/model/destination.dart';
import 'package:rogapp/pages/destination/destination_controller.dart'; import 'package:rogapp/pages/destination/destination_controller.dart';
import 'package:rogapp/pages/index/index_controller.dart'; import 'package:rogapp/pages/index/index_controller.dart';
import 'package:rogapp/pages/search/search_controller.dart'; import 'package:rogapp/pages/search/search_controller.dart';
import 'package:rogapp/widgets/bottom_sheet_new.dart'; import 'package:rogapp/widgets/bottom_sheets/bottom_sheet_start.dart';
import 'package:rogapp/widgets/bottom_sheets/bottom_sheet_goal.dart';
import 'package:rogapp/widgets/bottom_sheets/bottom_sheet_normal_point.dart';
class SearchPage extends StatelessWidget { class SearchPage extends StatelessWidget {
SearchPage({Key? key}) : super(key: key); SearchPage({Key? key}) : super(key: key);
@ -75,15 +77,22 @@ class SearchPage extends StatelessWidget {
Destination des = Destination des =
destinationController.festuretoDestination(suggestion); destinationController.festuretoDestination(suggestion);
Get.back(); Get.back();
Widget bottomSheet;
if (des.cp == -1 || des.cp == 0) {
bottomSheet = BottomSheetStart(destination: des);
} else if (des.cp == -2 || des.cp == 0) {
bottomSheet = BottomSheetGoal(destination: des);
} else {
bottomSheet = BottomSheetNormalPoint(destination: des);
}
showModalBottomSheet( showModalBottomSheet(
constraints: constraints:
BoxConstraints.loose(Size(Get.width, Get.height * 0.75)), BoxConstraints.loose(Size(Get.width, Get.height * 0.75)),
isScrollControlled: true, isScrollControlled: true,
context: context, context: context,
//builder: (context) => BottomSheetWidget(), builder: ((context) => bottomSheet)
builder: ((context) => BottomSheetNew( );
destination: des,
)));
}, },
suggestionsCallback: (pattern) async { suggestionsCallback: (pattern) async {
@ -106,29 +115,6 @@ class SearchPage extends StatelessWidget {
}, },
), ),
), ),
// Obx(() =>
// ListView.builder(
// itemCount: searchController.searchResults.length,
// itemBuilder: (context, index){
// return ListTile(
// title: searchController.searchResults[index].properties!["location_name"] != null ? Text(searchController.searchResults[index].properties!["location_name"]) : Text(""),
// subtitle: searchController.searchResults[index].properties!["category"] != null ? Text(searchController.searchResults[index].properties!["category"]) : Text(""),
// leading: getImage(index),
// onTap: (){
// indexController.currentFeature.clear();
// indexController.currentFeature.add(searchController.searchResults[index]);
// Get.back();
// showModalBottomSheet(
// isScrollControlled: true,
// context: context,
// //builder: (context) => BottomSheetWidget(),
// builder:((context) => BottomSheetNew())
// );
// },
// );
// },
// ),
// )
); );
} }
} }

View File

@ -45,20 +45,20 @@ class GpsDatabaseHelper {
Future<int> insertGps(GpsData gps) async { Future<int> insertGps(GpsData gps) async {
try { try {
print("---- try insering ${gps.toMap()}"); //print("---- try insering ${gps.toMap()}");
Database db = await instance.database; Database db = await instance.database;
int? nextOrder = int? nextOrder =
Sqflite.firstIntValue(await db.rawQuery('SELECT MAX(id) FROM gps')); Sqflite.firstIntValue(await db.rawQuery('SELECT MAX(id) FROM gps'));
nextOrder = nextOrder ?? 0; nextOrder = nextOrder ?? 0;
nextOrder = nextOrder + 1; nextOrder = nextOrder + 1;
gps.id = nextOrder; gps.id = nextOrder;
print("---- insering ${gps.toMap()}"); //print("---- insering ${gps.toMap()}");
int res = await db.insert( int res = await db.insert(
'gps', 'gps',
gps.toMap(), gps.toMap(),
conflictAlgorithm: ConflictAlgorithm.replace, conflictAlgorithm: ConflictAlgorithm.replace,
); );
print("------ database helper insert $res-----------::::::::"); //print("------ database helper insert $res-----------::::::::");
return res; return res;
} catch (err) { } catch (err) {
print("------ error $err-----------::::::::"); print("------ error $err-----------::::::::");

View File

@ -35,9 +35,6 @@ class LocationController extends GetxController {
StreamSubscription<Position>? positionStream; StreamSubscription<Position>? positionStream;
// 位置情報のストリームを保持する変数です。StreamSubscription<Position>型で宣言されています。 // 位置情報のストリームを保持する変数です。StreamSubscription<Position>型で宣言されています。
//===== Akira Added 2024-4-9 start
// GPSシミュレーション用の変数を追加
bool isSimulationMode = true;
LatLng? lastValidLocation; LatLng? lastValidLocation;
// GPSシミュレーション用のメソッドを追加 // GPSシミュレーション用のメソッドを追加
@ -45,13 +42,52 @@ class LocationController extends GetxController {
isSimulationMode = value; isSimulationMode = value;
} }
String latestSignalStrength="low"; // ====== Akira , GPS信号強度をシミュレート ==== ここから
//
//===== Akira Added 2024-4-9 start
// GPSシミュレーション用の変数を追加 ===> 本番では false にする。
bool isSimulationMode = true;
// GPS信号強度をシミュレートするための変数
final Rx<String> _simulatedSignalStrength = Rx<String>('low');
// GPS信号強度をシミュレートするための関数
void setSimulatedSignalStrength(String strength) {
if( strength!='real') {
isSimulationMode = true;
_simulatedSignalStrength.value = strength;
latestSignalStrength.value = strength;
}else{
isSimulationMode = false;
_simulatedSignalStrength.value = strength;
}
}
// シミュレートされた信号強度を取得するための関数
String getSimulatedSignalStrength() {
debugPrint("strength : ${_simulatedSignalStrength.value}");
return _simulatedSignalStrength.value;
}
//
// ====== Akira , GPS信号強度をシミュレート ==== ここまで
// GPS信号が弱い場合のフラグ. 本番では、true,high にする。
bool isGpsSignalWeak = true;
RxString latestSignalStrength = 'low'.obs;
//final _latestSignalStrength = 'low'.obs; // 初期値を設定
//String get latestSignalStrength => _latestSignalStrength.value;
Stream<String> get gpsSignalStrengthStream => latestSignalStrength.stream;
// GPS信号の強弱を判断するメソッドを追加. 10m 以内強、30m以内中、それ以上 // GPS信号の強弱を判断するメソッドを追加. 10m 以内強、30m以内中、それ以上
// //
String getGpsSignalStrength(Position? position) { String getGpsSignalStrength(Position? position) {
if (position == null) { if (position == null) {
latestSignalStrength="low"; latestSignalStrength.value = "low";
isGpsSignalWeak = true;
return 'low'; return 'low';
} }
final accuracy = position.accuracy; final accuracy = position.accuracy;
@ -59,13 +95,16 @@ class LocationController extends GetxController {
return _simulatedSignalStrength.value; // GPS信号強度シミュレーション return _simulatedSignalStrength.value; // GPS信号強度シミュレーション
}else { }else {
if (accuracy <= 10) { if (accuracy <= 10) {
latestSignalStrength="high"; latestSignalStrength.value = "high";
isGpsSignalWeak = false;
return 'high'; return 'high';
} else if (accuracy <= 30) { } else if (accuracy <= 30) {
latestSignalStrength="medium"; latestSignalStrength.value = "medium";
isGpsSignalWeak = false;
return 'medium'; return 'medium';
} else { } else {
latestSignalStrength="low"; latestSignalStrength.value = "low";
isGpsSignalWeak = true;
return 'low'; return 'low';
} }
} }
@ -74,11 +113,18 @@ class LocationController extends GetxController {
// 現在位置を調整するメソッドを追加 // 現在位置を調整するメソッドを追加
LatLng? adjustCurrentLocation(Position? position) { LatLng? adjustCurrentLocation(Position? position) {
if (position == null) { if (position == null) {
if( lastValidLocation!=null ) {
//debugPrint("=== adjustCurrentLocation (Position:Null and using LastValidLocation ${lastValidLocation})===");
return LatLng(lastValidLocation!.latitude, lastValidLocation!.longitude);
}else {
print("=== adjustCurrentLocation (Position:Null and No LastValidLocation ... )===");
return null; return null;
}
//return lastValidLocation ?? LatLng(0, 0); //return lastValidLocation ?? LatLng(0, 0);
} }
final signalStrength = getGpsSignalStrength(position); final signalStrength = getGpsSignalStrength(position);
if (signalStrength == 'high' || signalStrength == 'medium') { if (signalStrength == 'high' || signalStrength == 'medium') {
//debugPrint("=== adjustCurrentLocation (Position:Get and return Valid location:${position} ... )===");
lastValidLocation = LatLng(position.latitude, position.longitude); lastValidLocation = LatLng(position.latitude, position.longitude);
} }
return lastValidLocation ?? LatLng(position.latitude, position.longitude); return lastValidLocation ?? LatLng(position.latitude, position.longitude);
@ -93,25 +139,6 @@ class LocationController extends GetxController {
bool isStreamPaused = false; // 位置情報のストリームが一時停止中かどうかを示すフラグです。bool型で宣言されています。 bool isStreamPaused = false; // 位置情報のストリームが一時停止中かどうかを示すフラグです。bool型で宣言されています。
// ====== Akira , GPS信号強度をシミュレート ==== ここから
//
// GPS信号強度をシミュレートするための変数
final Rx<String> _simulatedSignalStrength = Rx<String>('high');
// GPS信号強度をシミュレートするための関数
void setSimulatedSignalStrength(String strength) {
_simulatedSignalStrength.value = strength;
}
// シミュレートされた信号強度を取得するための関数
String getSimulatedSignalStrength() {
return _simulatedSignalStrength.value;
}
//
// ====== Akira , GPS信号強度をシミュレート ==== ここから
// 位置マーカーの位置情報のストリームを取得するゲッター関数です。 // 位置マーカーの位置情報のストリームを取得するゲッター関数です。
// locationMarkerPositionStreamController.streamを返します。 // locationMarkerPositionStreamController.streamを返します。
// //
@ -223,7 +250,7 @@ class LocationController extends GetxController {
return; return;
} }
// 位置情報の設定を行います。 // 位置情報の設定を行います。z11
// Set up the location options // Set up the location options
const locationOptions = const locationOptions =
LocationSettings(accuracy: LocationAccuracy.high, distanceFilter: 0); LocationSettings(accuracy: LocationAccuracy.high, distanceFilter: 0);
@ -235,6 +262,13 @@ class LocationController extends GetxController {
// //
positionStream = Geolocator.getPositionStream(locationSettings: locationOptions).listen( positionStream = Geolocator.getPositionStream(locationSettings: locationOptions).listen(
(Position? position) { (Position? position) {
final signalStrength = getGpsSignalStrength(position);
if (signalStrength == 'low') {
isGpsSignalWeak = true;
} else {
isGpsSignalWeak = false;
}
final adjustedLocation = adjustCurrentLocation(position); final adjustedLocation = adjustCurrentLocation(position);
if (adjustedLocation != null) { if (adjustedLocation != null) {
final locationMarkerPosition = LocationMarkerPosition( final locationMarkerPosition = LocationMarkerPosition(
@ -242,30 +276,15 @@ class LocationController extends GetxController {
longitude: adjustedLocation.longitude, longitude: adjustedLocation.longitude,
accuracy: position?.accuracy ?? 0, accuracy: position?.accuracy ?? 0,
); );
locationMarkerPositionStreamController.add(locationMarkerPosition); handleLocationUpdate(locationMarkerPosition);
//locationMarkerPositionStreamController.add(locationMarkerPosition); // 位置データ送信
} else { } else {
// 位置情報が取得できなかった場合、 // 位置情報が取得できなかった場合、
// locationMarkerPositionStreamControllerにnullを追加します。 // locationMarkerPositionStreamControllerにnullを追加します。
locationMarkerPositionStreamController.add(null); locationMarkerPositionStreamController.add(null); // null 送信?
//forceUpdateLocation(Position? position);
} }
/*
if (position != null) {
// ストリームから位置情報を受信した場合、
// LocationMarkerPositionオブジェクトを作成し、
// locationMarkerPositionStreamControllerに追加します。
//
final LocationMarkerPosition locationMarkerPosition =
LocationMarkerPosition(
latitude: position.latitude,
longitude: position.longitude,
accuracy: position.accuracy);
locationMarkerPositionStreamController.add(locationMarkerPosition);
} else {
// 位置情報が取得できなかった場合、
// locationMarkerPositionStreamControllerにnullを追加します。
locationMarkerPositionStreamController.add(null);
}
*/
}, },
onError: (e) { onError: (e) {
// エラーが発生した場合、locationMarkerPositionStreamControllerにエラーを追加します。 // エラーが発生した場合、locationMarkerPositionStreamControllerにエラーを追加します。
@ -314,6 +333,36 @@ class LocationController extends GetxController {
} }
} }
void handleLocationUpdate(LocationMarkerPosition? position) async {
if (position != null) {
/*
currentPosition.value = position;
final locationMarkerPosition = LocationMarkerPosition(
latitude: position.latitude,
longitude: position.longitude,
accuracy: position.accuracy,
);
*/
locationMarkerPositionStreamController.add(position);
}
}
// このメソッドは、現在の位置情報を locationMarkerPositionStreamController に送信します。
//
void forceUpdateLocation(Position? position) {
if (position != null) {
final adjustedLocation = adjustCurrentLocation(position);
if (adjustedLocation != null) {
final locationMarkerPosition = LocationMarkerPosition(
latitude: adjustedLocation.latitude,
longitude: adjustedLocation.longitude,
accuracy: position.accuracy,
);
locationMarkerPositionStreamController.add(locationMarkerPosition);
}
}
}
// コントローラーのクローズ時に呼び出されるライフサイクルメソッドです。 // コントローラーのクローズ時に呼び出されるライフサイクルメソッドです。
// positionStreamをキャンセルします。 // positionStreamをキャンセルします。
// //

View File

@ -0,0 +1,325 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:geolocator/geolocator.dart';
import 'package:rogapp/model/destination.dart';
import 'package:rogapp/pages/index/index_controller.dart';
import 'package:rogapp/utils/const.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:rogapp/pages/destination/destination_controller.dart';
class BottomSheetBase extends StatelessWidget {
BottomSheetBase({super.key, required this.destination,this.isAlreadyCheckedIn = false});
final IndexController indexController = Get.find<IndexController>();
final DestinationController destinationController = Get.find<DestinationController>();
final bool isAlreadyCheckedIn; // すでにチェックイン済みかどうかのフラグ
final Destination destination; // 目的地オブジェクト
// そのポイントの写真を取得するメソッド
//
Image getImage() {
String serverUrl = ConstValues.currentServer();
// currentDestinationFeature(destinationそのもの)のphotoデータのチェック
// Nouffer のコードでは、indexController.rogMode によって、destination(currentDestinationFeature) かGeoJSONFeature(currentFeature)の違いで処理が分かれていた。
// しかし、現在のコードでは GeoJSONFeature gf = indexController.currentFeature[0]; は使われていない。
//
if (indexController.currentDestinationFeature[0].photos! == "") {
// 定義されていなければ、empty_image.png を選択
return const Image(image: AssetImage('assets/images/empty_image.png'));
} else {
String photo = indexController.currentDestinationFeature[0].photos!;
if (photo.contains('http')) {
// photo にhttpの文字が含まれていれば、ネットから拾ってくる。エラーならempty_image.pngを表示。
return Image(
image: NetworkImage(
indexController.currentDestinationFeature[0].photos!,
),
errorBuilder:
(BuildContext context, Object exception, StackTrace? stackTrace) {
return Image.asset("assets/images/empty_image.png");
},
);
} else {
// photo にhttpの文字が含まれていなければ、指定サーバー($serverUrl/media/compressed/*)から拾ってくる。エラーならempty_image.pngを表示。
return Image(
image: NetworkImage(
'$serverUrl/media/compressed/${indexController.currentDestinationFeature[0].photos!}',
),
errorBuilder:
(BuildContext context, Object exception, StackTrace? stackTrace) {
return Image.asset("assets/images/empty_image.png");
},
);
}
}
}
// URLを開くためのメソッドです。
// url_launcherパッケージを使用して、指定されたURLを開きます。
//
void _launchURL(url) async {
if (!await launchUrl(url)) throw 'Could not launch $url';
}
// 指定されたlocationidが目的地リストに含まれているかどうかを確認するメソッドです。
// destinationController.destinationsリストを走査し、locationidが一致する目的地があるかどうかを返します。
//
bool isInDestination(String locationid) {
int lid = int.parse(locationid);
if (destinationController.destinations
.where((element) => element.location_id == lid)
.isNotEmpty) {
return true;
} else {
return false;
}
}
@override
Widget build(BuildContext context) {
return SingleChildScrollView(
child: Column(
children: buildWidgets(context),
),
);
}
// このメソッドはList<Widget>を返します。
List<Widget> buildWidgets(BuildContext context) {
return [
Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
children: [
MaterialButton(
onPressed: () {
Get.back();
},
color: Colors.blue,
textColor: Colors.white,
padding: const EdgeInsets.all(16),
shape: const CircleBorder(),
child: const Icon(Icons.arrow_back_ios, size: 14),
),
Expanded(
child: Container(
alignment: Alignment.center,
child: Obx(() => Text("${destination.sub_loc_id} : ${destination.name}",
style: const TextStyle(fontSize: 15.0, fontWeight: FontWeight.bold))),
),
),
],
),
),
Row(
children: [Expanded(child: SizedBox(height: 260.0, child: Obx(() => getImage())))],
),
Obx(() => Padding(
padding: const EdgeInsets.all(8.0),
child: getDetails(context),
)),
const SizedBox(height: 60.0),
];
}
Widget getDetails(BuildContext context) {
return Column(
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
children: [
const Icon(Icons.roundabout_left),
const SizedBox(
width: 8.0,
),
destination.address != null && destination.address!.isNotEmpty
? getDetailsItem(
context,
"address".tr,
destination.address ?? '',
)
: const SizedBox(
width: 0.0,
height: 0,
),
],
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
children: [
const Icon(Icons.phone),
const SizedBox(
width: 8.0,
),
destination.phone != null && destination.phone!.isNotEmpty
? getDetailsItem(
context,
"telephone".tr,
destination.phone ?? '',
)
: const SizedBox(
width: 0.0,
height: 0,
),
],
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
children: [
const Icon(Icons.email),
const SizedBox(
width: 8.0,
),
destination.email != null && destination.email!.isNotEmpty
? getDetailsItem(
context,
"email".tr,
destination.email ?? '',
)
: const SizedBox(
width: 0.0,
height: 0,
),
],
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
children: [
const Icon(Icons.language),
const SizedBox(
width: 8.0,
),
destination.webcontents != null &&
destination.webcontents!.isNotEmpty
? getDetailsItem(
context,
"web".tr,
destination.webcontents ?? '',
isUrl: true,
)
: const SizedBox(
width: 0.0,
height: 0,
),
],
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
children: [
const SizedBox(
width: 8.0,
),
indexController.currentFeature[0]
.properties!["remark"] !=
null &&
(indexController.currentFeature[0]
.properties!["remark"] as String)
.isNotEmpty
? getDetailsItem(
context,
"remarks".tr,
indexController.currentFeature[0]
.properties!["remark"] ??
'',
isUrl: false)
: const SizedBox(
width: 0.0,
height: 0,
),
],
),
),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: Theme.of(context).colorScheme.onPrimaryContainer),
onPressed: () async {
// print(
// "dist to start ${destinationController.distanceToStart()}");
Get.back();
//print("---- go to ----");
// GeoJSONMultiPoint mp = indexController
// .currentFeature[0] as GeoJSONMultiPoint;
Position position =
await Geolocator.getCurrentPosition(
desiredAccuracy:
LocationAccuracy.bestForNavigation,
forceAndroidLocationManager: true);
//print("------- position -------- $position");
Destination ds = Destination(
lat: position.latitude,
lon: position.longitude);
Destination tp = Destination(
lat: destination.lat, lon: destination.lon);
destinationController
.destinationMatrixFromCurrentPoint([ds, tp]);
// TODO: Implement "ここへ行く" functionality
},
child: Text(
"ルート",
style: TextStyle(
color: Theme.of(context).colorScheme.onPrimary,
),
),
),
const SizedBox(
width: 10,
),
],
),
],
);
}
Widget getDetailsItem(BuildContext context, String label, String text,
{bool isUrl = false}) {
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(label),
const SizedBox(
width: 10.0,
),
InkWell(
onTap: () {
if (isUrl) {
_launchURL(destination.webcontents);
}
},
child: SizedBox(
width: MediaQuery.of(context).size.width -
(MediaQuery.of(context).size.width * 0.35),
child: Text(
text,
textAlign: TextAlign.justify,
style: TextStyle(
color: isUrl ? Colors.blue : Colors.black,
),
),
),
),
],
);
}
}

View File

@ -0,0 +1,34 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:rogapp/pages/destination/destination_controller.dart';
import 'package:rogapp/widgets/bottom_sheets/bottom_sheet_normal_point.dart';
class BottomSheetBuyPoint extends BottomSheetNormalPoint {
BottomSheetBuyPoint({super.key, required super.destination});
final DestinationController destinationController = Get.find<DestinationController>();
@override
List<Widget> buildWidgets(BuildContext context) {
// super.buildWidgets(context) を継承して、追加のウィジェットをリストに組み込む
return [
...super.buildWidgets(context),
ElevatedButton(
onPressed: () async {
// 「買い物チェックイン」ボタンがタップされた際の処理
// TODO: Implement QR code scanning and buy point processing logic
},
child: const Text('買い物チェックイン'),
),
];
}
@override
Widget build(BuildContext context) {
return SingleChildScrollView(
child: Column(
children: buildWidgets(context),
),
);
}
}

View File

@ -0,0 +1,69 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:rogapp/model/destination.dart';
import 'package:rogapp/pages/destination/destination_controller.dart';
import 'package:rogapp/widgets/bottom_sheets/bottom_sheet_base.dart';
import 'package:latlong2/latlong.dart';
import 'package:rogapp/pages/camera/camera_page.dart';
import 'dart:ui' as ui;
class BottomSheetGoal extends BottomSheetBase {
BottomSheetGoal({super.key, required super.destination});
final DestinationController destinationController = Get.find<DestinationController>();
@override
List<Widget> buildWidgets(BuildContext context) {
// super.buildWidgets(context) を継承して、追加のウィジェットをリストに組み込む
return [
...super.buildWidgets(context),
ElevatedButton(
onPressed: destinationController.distanceToStart() <= 100
? () async {
// 「ゴール時間撮影」ボタンがタップされた際の処理
Destination cdest = destinationController.festuretoDestination(indexController.currentFeature[0]);
var distance = const Distance();
double distanceToDest = distance.as(
LengthUnit.Meter,
LatLng(
destinationController.currentLat, destinationController.currentLon),
LatLng(cdest.lat!, cdest.lon!));
if (destinationController.rogainingCounted.value == true &&
// destinationController.distanceToStart() <= 500 && ... GPS信号が弱い時でもOKとする。
(destinationController.distanceToStart() <= 500 || destinationController.isGpsSignalWeak() ) &&
destination.cp == -1 &&
DestinationController.ready_for_goal == true) {
destinationController.isAtGoal.value = true;
destinationController.photos.clear();
await showModalBottomSheet(
constraints: BoxConstraints.loose(
ui.Size(Get.width, Get.height * 0.75)),
context: Get.context!,
isScrollControlled: true,
builder: ((context) => CameraPage(
destination: destination,
))).whenComplete(() {
destinationController.skipGps = false;
destinationController.chekcs = 0;
destinationController.isAtGoal.value = false;
});
}
}
: null,
child: const Text('ゴール時間撮影'),
),
];
}
@override
Widget build(BuildContext context) {
return SingleChildScrollView(
child: Column(
children: buildWidgets(context),
),
);
}
}

View File

@ -0,0 +1,64 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:rogapp/pages/destination/destination_controller.dart';
import 'package:rogapp/widgets/bottom_sheets/bottom_sheet_base.dart';
class BottomSheetNormalPoint extends BottomSheetBase {
BottomSheetNormalPoint({super.key, required super.destination});
final DestinationController destinationController = Get.find<DestinationController>();
@override
List<Widget> buildWidgets(BuildContext context) {
// super.buildWidgets(context) を継承して、追加のウィジェットをリストに組み込む
return [
...super.buildWidgets(context),
ElevatedButton(
onPressed: destinationController.distanceToStart() <= 100
? () async {
if (destinationController.rogainingCounted.value == true &&
(destinationController.distanceToStart() <= 500 ||
destinationController.isGpsSignalWeak()) &&
destination.cp! > 0) {
// isAlreadyCheckedIn == true
// 「チェックイン撮影」ボタンがタップされた際の処理
try {
Get.back();
await destinationController.callforCheckin(destination);
// ここで、CameraPageへの遷移が行われる。
} catch (e) {
// エラーハンドリング
Get.snackbar(
'Error',
'An error occurred while processing check-in.',
backgroundColor: Colors.red,
colorText: Colors.white,
duration: Duration(seconds: 3),
);
// 必要に応じてエラーログを記録
print('Error processing check-in: $e');
}
} else if (destination.checkedin == true) { // 取り消しの場合?
;
}
}
: null,
child: Obx( () => Text(
destinationController.isInRog.value ? 'チェックイン取消' : 'チェックイン',
),
),
),
];
}
@override
Widget build(BuildContext context) {
return SingleChildScrollView(
child: Column(
children: buildWidgets(context),
),
);
}
}

View File

@ -0,0 +1,102 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:rogapp/pages/destination/destination_controller.dart';
import 'package:rogapp/widgets/bottom_sheets/bottom_sheet_base.dart';
import 'package:rogapp/main.dart';
import 'package:rogapp/services/external_service.dart';
class BottomSheetStart extends BottomSheetBase {
BottomSheetStart({super.key, required super.destination});
final DestinationController destinationController = Get.find<DestinationController>();
@override
List<Widget> buildWidgets(BuildContext context) {
// super.buildWidgets(context) を継承して、追加のウィジェットをリストに組み込む
return [
...super.buildWidgets(context),
ElevatedButton(
onPressed: destinationController.distanceToStart() <= 100
? () async {
// ボタンがタップされた際の処理
// Check conditions to show confirmation dialog
if (destinationController.isInRog.value == false &&
(destinationController.distanceToStart() <= 500 || destinationController.isGpsSignalWeak() ) && //追加 Akira 2024-4-5
(destination.cp == -1 || destination.cp == 0 ) &&
destinationController.rogainingCounted.value == false) {
// ロゲがまだ開始されていない: destinationController.isInRog.value == false
// かつ (開始ポイントまでの距離が 500m 以内 destinationController.distanceToStart() <= 500
// または GPS信号強度が弱い場合 destinationController.isGpsSignalWeak()
// ) かつ
// そのポイントのCP番号が -1 または 0 の場合:
// かつ ポイントがカウントされていない場合 destinationController.rogainingCounted.value=false
// にロゲイニング開始ができる。
//
print("counted ${destinationController.rogainingCounted.value}");
// Show confirmation dialog
Get.dialog(
AlertDialog(
title: const Text("確認"), //confirm
content: const Text(
"ロゲを開始すると、今までのロゲデータが全てクリアされます。本当に開始しますか?"), //are you sure
actions: <Widget>[
TextButton(
child: const Text("いいえ"), //no
onPressed: () {
Get.back(); // Close the dialog
},
),
TextButton(
child: const Text("はい"), //yes
onPressed: () async {
// Clear data and start game logic here
destinationController.isInRog.value = true;
destinationController.resetRogaining();
destinationController.addToRogaining(
destinationController.currentLat,
destinationController.currentLon,
destination.location_id!,
);
saveGameState(); // main.dart 参照 // ゲームの状態を保存する。
await ExternalService().startRogaining();
Get.back(); // Close the dialog and potentially navigate away
},
),
],
),
barrierDismissible:
false, // User must tap a button to close the dialog
);
} else if (destinationController.isInRog.value == true &&
destination.cp != -1 && destination.cp != 0 ) {
//print("counted ${destinationController.rogainingCounted.value}");
// Existing logic for other conditions
Get.back();
await destinationController.callforCheckin(destination);
} else {
return;
}
}
: null,
child: Obx(
() => Text(
destinationController.isInRog.value ? '競技中' : 'ロゲ開始',
),
),
),
];
}
@override
Widget build(BuildContext context) {
return SingleChildScrollView(
child: Column(
children: buildWidgets(context),
),
);
}
}

View File

@ -0,0 +1,43 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:rogapp/model/destination.dart';
import 'package:rogapp/pages/destination/destination_controller.dart';
import 'package:rogapp/widgets/bottom_sheets/bottom_sheet_base.dart';
class BottomSheetStartGoal extends BottomSheetBase {
BottomSheetStartGoal({Key? key, required Destination destination})
: super(key: key, destination: destination);
final DestinationController destinationController =
Get.find<DestinationController>();
@override
List<Widget> buildWidgets(BuildContext context) {
// super.buildWidgets(context) を継承して、追加のウィジェットをリストに組み込む
return [
...super.buildWidgets(context),
ElevatedButton(
onPressed: destinationController.distanceToStart() <= 100
? () async {
// 「ロゲ開始」または「ゴール時間撮影」ボタンがタップされた際の処理
// TODO: Implement start game or goal time capture logic based on game state
}
: null,
child: Obx(
() => Text(
destinationController.isInRog.value ? 'ゴール時間撮影' : 'ロゲ開始',
),
),
),
];
}
@override
Widget build(BuildContext context) {
return SingleChildScrollView(
child: Column(
children: buildWidgets(context),
),
);
}
}

View File

@ -5,7 +5,9 @@ import 'package:rogapp/pages/destination/destination_controller.dart';
import 'package:rogapp/pages/index/index_controller.dart'; import 'package:rogapp/pages/index/index_controller.dart';
import 'package:rogapp/utils/const.dart'; import 'package:rogapp/utils/const.dart';
import 'package:rogapp/utils/database_helper.dart'; import 'package:rogapp/utils/database_helper.dart';
import 'package:rogapp/widgets/bottom_sheet_new.dart'; import 'package:rogapp/widgets/bottom_sheets/bottom_sheet_start.dart';
import 'package:rogapp/widgets/bottom_sheets/bottom_sheet_goal.dart';
import 'package:rogapp/widgets/bottom_sheets/bottom_sheet_normal_point.dart';
import 'package:timeline_tile/timeline_tile.dart'; import 'package:timeline_tile/timeline_tile.dart';
class DestinationWidget extends StatelessWidget { class DestinationWidget extends StatelessWidget {
@ -192,15 +194,23 @@ class DestinationWidget extends StatelessWidget {
// "--- ndexController.currentDestinationFeature ----- ${indexController.currentDestinationFeature[0].name} ----"); // "--- ndexController.currentDestinationFeature ----- ${indexController.currentDestinationFeature[0].name} ----");
//indexController.getAction(); //indexController.getAction();
Widget bottomSheet;
if (fs.cp == -1 || fs.cp == 0) {
bottomSheet = BottomSheetStart(destination: fs);
} else if (fs.cp == -2 || fs.cp == 0) {
bottomSheet = BottomSheetGoal(destination: fs);
} else {
bottomSheet = BottomSheetNormalPoint(destination: fs);
}
showModalBottomSheet( showModalBottomSheet(
constraints: BoxConstraints.loose( constraints: BoxConstraints.loose(
Size(Get.width, Get.height * 0.85)), Size(Get.width, Get.height * 0.85)),
context: context, context: context,
isScrollControlled: true, isScrollControlled: true,
//builder:((context) => BottomSheetWidget()) //builder:((context) => BottomSheetWidget())
builder: ((context) => BottomSheetNew( builder: ((context) => bottomSheet)
destination: fs, );
)));
} }
}, },
onLongPress: () { onLongPress: () {

View File

@ -10,7 +10,7 @@ class GpsSignalStrengthIndicator extends StatelessWidget {
// コンストラクタにminimizedパラメータを追加し、デフォルト値をfalseに設定 // コンストラクタにminimizedパラメータを追加し、デフォルト値をfalseに設定
GpsSignalStrengthIndicator({ GpsSignalStrengthIndicator({
Key? key, super.key,
required this.locationController, required this.locationController,
this.minimized = false, // ここでデフォルト値を指定 this.minimized = false, // ここでデフォルト値を指定
}); });
@ -19,8 +19,8 @@ class GpsSignalStrengthIndicator extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
// final LocationController locationController = Get.find<LocationController>(); // final LocationController locationController = Get.find<LocationController>();
return Obx(() { return Obx(() {
String signalStrength = locationController.latestSignalStrength; String signalStrength = locationController.latestSignalStrength.value;
print("signalStrength=${signalStrength}"); debugPrint("GpsSignalStrengthIndicator : signalStrength=${signalStrength}");
IconData iconData; IconData iconData;
Color backgroundColor; Color backgroundColor;
String text; String text;

View File

@ -6,7 +6,9 @@ import 'package:rogapp/pages/destination/destination_controller.dart';
import 'package:rogapp/pages/index/index_controller.dart'; import 'package:rogapp/pages/index/index_controller.dart';
import 'package:rogapp/services/maxtrix_service.dart'; import 'package:rogapp/services/maxtrix_service.dart';
import 'package:rogapp/utils/const.dart'; import 'package:rogapp/utils/const.dart';
import 'package:rogapp/widgets/bottom_sheet_new.dart'; import 'package:rogapp/widgets/bottom_sheets/bottom_sheet_start.dart';
import 'package:rogapp/widgets/bottom_sheets/bottom_sheet_goal.dart';
import 'package:rogapp/widgets/bottom_sheets/bottom_sheet_normal_point.dart';
class ListWidget extends StatefulWidget { class ListWidget extends StatefulWidget {
const ListWidget({Key? key}) : super(key: key); const ListWidget({Key? key}) : super(key: key);
@ -15,6 +17,15 @@ class ListWidget extends StatefulWidget {
State<ListWidget> createState() => _ListWidgetState(); State<ListWidget> createState() => _ListWidgetState();
} }
// IndexControllerから目的地のリストを取得し、ListView.builderを使用してリストを表示します。
// 各リストアイテムは、目的地の画像、名前、カテゴリ、サブロケーションID、現在地からの距離を表示します。
// リストアイテムがタップされると、changeCurrentFeatureメソッドを呼び出して現在の目的地を更新し、 BottomSheetウィジェットを表示します。
// 主なロジック:
// IndexControllerから目的地のリストを取得し、ListView.builderを使用してリストを構築します。
// getImageメソッドを使用して、目的地の画像を取得し表示します。画像が存在しない場合は、デフォルトの画像を表示します。
// matrixDistanceメソッドを使用して、現在地から目的地までの距離を計算し表示します。
// リストアイテムがタップされると、changeCurrentFeatureメソッドを呼び出して現在の目的地を更新し、showModalBottomSheetを使用してBottomSheetウィジェットを表示します。
//
class _ListWidgetState extends State<ListWidget> { class _ListWidgetState extends State<ListWidget> {
final IndexController indexController = Get.find<IndexController>(); final IndexController indexController = Get.find<IndexController>();
@ -42,6 +53,7 @@ class _ListWidgetState extends State<ListWidget> {
} }
} }
// 未使用?
void changeCurrentFeature(GeoJSONFeature fs) { void changeCurrentFeature(GeoJSONFeature fs) {
if (indexController.currentFeature.isNotEmpty) { if (indexController.currentFeature.isNotEmpty) {
indexController.currentFeature.clear(); indexController.currentFeature.clear();
@ -117,15 +129,23 @@ class _ListWidgetState extends State<ListWidget> {
Destination des = Destination des =
destinationController.festuretoDestination(gf); destinationController.festuretoDestination(gf);
changeCurrentFeature(gf); changeCurrentFeature(gf);
Widget bottomSheet;
if (des.cp == -1 || des.cp == 0) {
bottomSheet = BottomSheetStart(destination: des);
} else if (des.cp == -2 || des.cp == 0) {
bottomSheet = BottomSheetGoal(destination: des);
} else {
bottomSheet = BottomSheetNormalPoint(destination: des);
}
showModalBottomSheet( showModalBottomSheet(
constraints: BoxConstraints.loose( constraints: BoxConstraints.loose(
Size(Get.width, Get.height * 0.85)), Size(Get.width, Get.height * 0.85)),
isScrollControlled: true, isScrollControlled: true,
context: context, context: context,
//builder: (context) => BottomSheetWidget(), builder: ((context) => bottomSheet ),
builder: ((context) => BottomSheetNew( );
destination: des,
)));
}, },
leading: getImage(index), leading: getImage(index),
title: indexController.locations[0].features[index]! title: indexController.locations[0].features[index]!

View File

@ -2,7 +2,6 @@ import 'dart:async';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_map/flutter_map.dart'; import 'package:flutter_map/flutter_map.dart';
import 'package:flutter_map_location_marker/flutter_map_location_marker.dart'; import 'package:flutter_map_location_marker/flutter_map_location_marker.dart';
import 'package:flutter_map_marker_cluster/flutter_map_marker_cluster.dart';
import 'package:flutter_polyline_points/flutter_polyline_points.dart'; import 'package:flutter_polyline_points/flutter_polyline_points.dart';
import 'package:geojson_vi/geojson_vi.dart'; import 'package:geojson_vi/geojson_vi.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
@ -17,22 +16,8 @@ import 'package:rogapp/widgets/base_layer_widget.dart';
import 'package:rogapp/widgets/bottom_sheet_new.dart'; import 'package:rogapp/widgets/bottom_sheet_new.dart';
import 'package:rogapp/widgets/current_position_widget.dart'; import 'package:rogapp/widgets/current_position_widget.dart';
import 'package:rogapp/widgets/game_state_view.dart'; import 'package:rogapp/widgets/game_state_view.dart';
import 'package:flutter_map_tile_caching/flutter_map_tile_caching.dart';
// map_widget.dartファイルは、アプリ内の地図表示を担当するウィジェットを定義しています。以下に、主要な部分を解説します。
// 地図表示に関連する主要な機能を提供しています。以下のような機能が含まれています。
//
// 地図の表示と操作
// マーカーの表示とカスタマイズ
// ルートの表示
// 現在位置の表示
// アイドル状態の処理
// ローディングインジケーターの表示
// ゲーム状態の表示
// 現在位置ボタンの表示
// StatefulWidgetを継承したクラスで、地図表示のメインウィジェットです。
//
class MapWidget extends StatefulWidget { class MapWidget extends StatefulWidget {
const MapWidget({Key? key}) : super(key: key); const MapWidget({Key? key}) : super(key: key);
@ -40,36 +25,131 @@ class MapWidget extends StatefulWidget {
State<MapWidget> createState() => _MapWidgetState(); State<MapWidget> createState() => _MapWidgetState();
} }
// MapWidgetの状態を管理するクラスです。
//
class _MapWidgetState extends State<MapWidget> { class _MapWidgetState extends State<MapWidget> {
final IndexController indexController = Get.find<IndexController>(); final IndexController indexController = Get.find<IndexController>();
// IndexControllerのインスタンスを保持します。
final DestinationController destinationController = final DestinationController destinationController =
Get.find<DestinationController>(); Get.find<DestinationController>();
// DestinationControllerのインスタンスを保持します。
final LocationController locationController = Get.find<LocationController>(); final LocationController locationController = Get.find<LocationController>();
// LocationControllerのインスタンスを保持します。
StreamSubscription? subscription; // 地図イベントの購読を保持します。 late MapController mapController;
Timer? _timer; // アイドル状態のタイマーを保持します。 final Completer<MapController> mapControllerCompleter = Completer<MapController>();
// 地図上のマーカーのUIを生成するメソッドです。 StreamSubscription? subscription;
// GeoJSONFeatureを受け取り、マーカーのUIを返します。 Timer? _timer;
//
Widget getMarkerShape(GeoJSONFeature i, BuildContext context) { Map<LatLng, Marker> _markerCache = {};
List<Marker> _markers = [];
@override
void initState() {
super.initState();
_startIdleTimer();
mapController = MapController();
indexController.mapController = mapController;
//_initMarkers();
// indexController.mapController = MapController(initCompleter: mapControllerCompleter);
}
@override
void dispose() {
mapController?.dispose();
_timer?.cancel();
super.dispose();
}
void _startIdleTimer() {
_timer = Timer(const Duration(milliseconds: (1000 * 10)), _centerMapOnUser);
}
void _resetTimer() {
_timer?.cancel();
_startIdleTimer();
}
void _centerMapOnUser() {
destinationController.centerMapToCurrentLocation();
}
Future<void> _initMarkers() async {
List<Marker> markers = await _getMarkers();
setState(() {
_markers = markers;
});
}
Future<List<Marker>> _getMarkers() async {
List<Marker> markers = [];
if (indexController.locations.isNotEmpty && indexController.locations[0].features.isNotEmpty) {
for (var feature in indexController.locations[0].features) {
GeoJSONMultiPoint point = feature!.geometry as GeoJSONMultiPoint;
LatLng latLng = LatLng(point.coordinates[0][1], point.coordinates[0][0]);
markers.add(Marker(
point: latLng,
width: 30.0,
height: 30.0,
child: getMarkerShape(feature),
));
/*
if (_markerCache.containsKey(latLng)) {
markers.add(_markerCache[latLng]!);
} else {
Marker marker = Marker(
point: latLng,
width: 30.0,
height: 30.0,
child: getMarkerShape(feature),
// child: getMarkerShape(feature, context),
);
_markerCache[latLng] = marker;
markers.add(marker);
}
*/
}
}
return markers;
}
/*
Future<List<Marker>> _getMarkers() async {
List<Marker> markers = [];
for (var feature in indexController.locations[0].features) {
GeoJSONMultiPoint point = feature!.geometry as GeoJSONMultiPoint;
LatLng latLng = LatLng(point.coordinates[0][1], point.coordinates[0][0]);
if (_markerCache.containsKey(latLng)) {
markers.add(_markerCache[latLng]!);
} else {
Marker marker = Marker(
point: latLng,
width: 30.0,
height: 30.0,
builder: (ctx) => getMarkerShape(feature, ctx),
//child: null,
);
_markerCache[latLng] = marker;
markers.add(marker);
}
}
return markers;
}
*/
// Widget getMarkerShape(GeoJSONFeature i, BuildContext context) {
Widget getMarkerShape(GeoJSONFeature i) {
final String labelText = TextUtils.getDisplayTextFeture(i);
//final double maxWidth = labelText.length * 40.0;
GeoJSONMultiPoint p = i.geometry as GeoJSONMultiPoint; GeoJSONMultiPoint p = i.geometry as GeoJSONMultiPoint;
//print("lat is ${p.geoSerie!.geoPoints[0].latitude} and lon is ${p.geoSerie!.geoPoints[0].longitude}");
//RegExp regex = RegExp(r'([.]*0)(?!.*\d)');
return InkWell( return InkWell(
onTap: () { onTap: () {
GeoJSONFeature? fs = indexController.getFeatureForLatLong( GeoJSONFeature? fs = indexController.getFeatureForLatLong(
p.coordinates[0][1], p.coordinates[0][0]); p.coordinates[0][1], p.coordinates[0][0]);
//print("------- fs $fs------");
if (fs != null) { if (fs != null) {
indexController.currentFeature.clear(); indexController.currentFeature.clear();
indexController.currentFeature.add(fs); indexController.currentFeature.add(fs);
//print("----- fs is ${fs.properties!['photos']}");
Destination des = destinationController.festuretoDestination(fs); Destination des = destinationController.festuretoDestination(fs);
@ -83,8 +163,7 @@ class _MapWidgetState extends State<MapWidget> {
isScrollControlled: true, isScrollControlled: true,
isDismissible: true, isDismissible: true,
builder: ((context) => BottomSheetNew( builder: ((context) => BottomSheetNew(
destination: des, isAlreadyCheckedIn: value.isNotEmpty)) destination: des, isAlreadyCheckedIn: value.isNotEmpty)),
//builder:((context) => BottomSheetWidget())
).whenComplete(() { ).whenComplete(() {
destinationController.shouldShowBottomSheet = true; destinationController.shouldShowBottomSheet = true;
destinationController.skipGps = false; destinationController.skipGps = false;
@ -94,7 +173,6 @@ class _MapWidgetState extends State<MapWidget> {
}, },
child: Stack( child: Stack(
fit: StackFit.expand, fit: StackFit.expand,
//mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
Container( Container(
height: 32, height: 32,
@ -103,11 +181,11 @@ class _MapWidgetState extends State<MapWidget> {
shape: BoxShape.circle, shape: BoxShape.circle,
color: Colors.transparent, color: Colors.transparent,
border: Border.all( border: Border.all(
color: i.properties!['buy_point'] > 0 color: i.properties!['buy_point'] > 0 ? Colors.blue : Colors.red,
? Colors.blue
: Colors.red,
width: 3, width: 3,
style: BorderStyle.solid)), style: BorderStyle.solid,
),
),
child: Stack( child: Stack(
alignment: Alignment.center, alignment: Alignment.center,
children: [ children: [
@ -126,50 +204,55 @@ class _MapWidgetState extends State<MapWidget> {
Icons.play_arrow_outlined, Icons.play_arrow_outlined,
color: Colors.red, color: Colors.red,
size: 70, size: 70,
)), ),
),
) )
: Container( : Container(
color: Colors.transparent, color: Colors.transparent,
), ),
], ],
)), ),
),
Transform.translate( Transform.translate(
offset: const Offset(45, 0), offset: const Offset(30, 0),
child: Align( child: Align(
alignment: Alignment.center, alignment: Alignment.center,
child: Container( child: Container(
color: Colors.purple.withOpacity(0.2), color: Colors.transparent,
// child: Text(TextUtils.getDisplayTextFeture(i), constraints: const BoxConstraints(maxWidth: 500), // 最大幅を設定
// style: const TextStyle( //constraints: BoxConstraints(maxWidth: maxWidth), // 最大幅を設定
// fontSize: 16, //color: Colors.purple.withOpacity(0.2),
// fontWeight: FontWeight.bold,
// color: Colors.red,
// ))),
child: Stack( child: Stack(
children: <Widget>[ children: <Widget>[
// Text with white outline
Text( Text(
TextUtils.getDisplayTextFeture(i), labelText,
style: TextStyle( style: TextStyle(
fontSize: 16, fontSize: 16,
fontWeight: FontWeight.w700, fontWeight: FontWeight.w700,
foreground: Paint() foreground: Paint()
..style = PaintingStyle.stroke ..style = PaintingStyle.stroke
..strokeWidth = 1 ..strokeWidth = 2
..color = Colors.white, ..color = Colors.white,
), ),
overflow: TextOverflow.ellipsis, // テキストが長すぎる場合に`...`で省略
//softWrap: true, // 複数行に渡って表示
//overflow: TextOverflow.visible, // テキストが切れないように
), ),
// Text with black fill
Text( Text(
TextUtils.getDisplayTextFeture(i), labelText,
//TextUtils.getDisplayTextFeture(i),
style: const TextStyle( style: const TextStyle(
fontSize: 16, fontSize: 16,
fontWeight: FontWeight.w700, fontWeight: FontWeight.w700,
color: Colors.black, color: Colors.black,
), ),
overflow: TextOverflow.ellipsis, // テキストが長すぎる場合に`...`で省略
//softWrap: true, // 複数行に渡って表示
//overflow: TextOverflow.visible, // テキストが切れないように
), ),
], ],
)), ),
),
), ),
) )
], ],
@ -177,11 +260,7 @@ class _MapWidgetState extends State<MapWidget> {
); );
} }
// ルートポイントをLatLngのリストに変換するメソッドです。
// IndexControllerのroutePointsからLatLngのリストを生成しています。
//
List<LatLng>? getPoints() { List<LatLng>? getPoints() {
//print("##### --- route point ${indexController.routePoints.length}");
List<LatLng> pts = []; List<LatLng> pts = [];
for (PointLatLng p in indexController.routePoints) { for (PointLatLng p in indexController.routePoints) {
LatLng l = LatLng(p.latitude, p.longitude); LatLng l = LatLng(p.latitude, p.longitude);
@ -190,87 +269,27 @@ class _MapWidgetState extends State<MapWidget> {
return pts; return pts;
} }
// ウィジェットの初期化時に呼び出されるメソッドです。
// _startIdleTimerメソッドを呼び出して、アイドル状態のタイマーを開始します。
//
@override
void initState() {
super.initState();
_startIdleTimer(); // _startIdleTimerメソッドを呼び出してアイドル状態のタイマーを開始しています。
}
// アイドル状態のタイマーを開始するメソッドです。
// 一定時間後に_centerMapOnUserメソッドを呼び出すようにタイマーを設定しています。
//
void _startIdleTimer() {
_timer = Timer(const Duration(milliseconds: (1000 * 10)), _centerMapOnUser);
}
// アイドル状態のタイマーをリセットするメソッドです。
//
void _resetTimer() {
_timer?.cancel();
_startIdleTimer();
}
// 地図をユーザーの現在位置に中央揃えするメソッドです。
//
void _centerMapOnUser() {
assert(() {
print("showBottomSheet ${destinationController.shouldShowBottomSheet}");
return true;
}());
// 2024-04-03 Akira Log enabled only debug mode..
//
//if (destinationController.shouldShowBottomSheet) {
destinationController.centerMapToCurrentLocation();
//}
}
// ウィジェットのUIを構築するメソッドです。
// FlutterMapウィジェットを使用して地図を表示します。
// layersプロパティに、ベースレイヤー、ルートレイヤー、現在位置レイヤー、マーカーレイヤーを設定します。
// PopupControllerを使用して、ポップアップの制御を行います。
// IndexControllerのisLoading変数に基づいて、ローディングインジケーターを表示します。
// GameStateWidgetとCurrentPositionウィジェットを重ねて表示します。
//
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
// print( //final PopupController popupController = PopupController();
// "---------- rog mode is ${indexController.rog_mode.value.toString()}----------");
final PopupController popupController = PopupController();
return Stack( return Stack(
children: [ children: [
// IndexControllerのisLoading変数に基づいて、ローディングインジケーターを表示します。
// isLoadingがtrueの場合はCircularProgressIndicatorを表示し、falseの場合は地図を表示します。
Obx(() => indexController.isLoading.value == true Obx(() => indexController.isLoading.value == true
? const Padding( ? const Padding(
padding: EdgeInsets.only(top: 60.0), padding: EdgeInsets.only(top: 60.0),
child: CircularProgressIndicator(), child: CircularProgressIndicator(),
) )
: FlutterMap( : FlutterMap(
// 地図の表示を担当 mapController: mapController,
mapController: indexController.mapController, //mapController: indexController.mapController,
options: MapOptions( options: MapOptions(
// 地図の初期設定(最大ズームレベル、初期位置、ズームレベルなど)を行っています。
maxZoom: 18.4, maxZoom: 18.4,
onMapReady: () { onMapReady: () {
// print("Map controller ready!!"); ... working corretly _initMarkers();
indexController.isMapControllerReady.value = true; // Added Akira,2024-4-6 for #2800 //indexController.isMapControllerReady.value = true;
subscription = indexController.mapController.mapEventStream
.listen((MapEvent mapEvent) {
if (mapEvent is MapEventMoveStart) {
// print(DateTime.now().toString() +
// ' [MapEventMoveStart] START');
// do something
}
if (mapEvent is MapEventMoveEnd) {}
});
}, },
initialCenter:
initialCenter: const LatLng(37.15319600454702, 139.58765950528198), const LatLng(37.15319600454702, 139.58765950528198),
bounds: indexController.currentBound.isNotEmpty bounds: indexController.currentBound.isNotEmpty
? indexController.currentBound[0] ? indexController.currentBound[0]
: LatLngBounds.fromPoints([ : LatLngBounds.fromPoints([
@ -280,42 +299,39 @@ class _MapWidgetState extends State<MapWidget> {
initialZoom: 1, initialZoom: 1,
interactiveFlags: interactiveFlags:
InteractiveFlag.pinchZoom | InteractiveFlag.drag, InteractiveFlag.pinchZoom | InteractiveFlag.drag,
onPositionChanged: (MapPosition pos, hasGesture) { onPositionChanged: (MapPosition pos, hasGesture) {
// 地図の位置が変更された際の処理を行います。
//print("map position changed ${pos.center!.latitude}");
if (hasGesture) { if (hasGesture) {
_resetTimer(); _resetTimer();
} }
indexController.currentBound = [pos.bounds!]; indexController.currentBound = [pos.bounds!];
}, },
onTap: (_, __) => popupController onMapEvent: (MapEvent mapEvent) {
.hideAllPopups(), // Hide popup when the map is tapped. if (mapEvent is MapEventMove) {
destinationController.shouldShowBottomSheet = true;
}
},
//onTap: (_, __) => popupController.hideAllPopups(),
), ),
children: [ children: [
const BaseLayer(), const BaseLayer(),
Obx( Obx(
() => indexController.routePointLenght > 0 () => indexController.routePointLenght > 0
? PolylineLayer( ? PolylineLayer(
// ルートの表示を担当
polylines: [ polylines: [
Polyline( Polyline(
points: getPoints()!, // ルートのポイントを設定しています。 points: getPoints()!,
strokeWidth: 6.0, strokeWidth: 6.0,
color: Colors.indigo), color: Colors.indigo,
),
], ],
) )
: Container(), : Container(),
), ),
CurrentLocationLayer( CurrentLocationLayer(
// 現在位置の表示を担当
positionStream: locationController positionStream: locationController
.locationMarkerPositionStreamController.stream, .locationMarkerPositionStreamController.stream,
// locationMarkerPositionStreamController.streamを設定して、現在位置の更新を監視しています。 //alignDirectionOnUpdate: AlignOnUpdate.never,
alignDirectionOnUpdate: AlignOnUpdate.never,
//turnOnHeadingUpdate: TurnOnHeadingUpdate.never,
style: const LocationMarkerStyle( style: const LocationMarkerStyle(
// styleプロパティで、現在位置のマーカーのスタイルを設定しています。
marker: Stack( marker: Stack(
children: [ children: [
CircleAvatar( CircleAvatar(
@ -323,70 +339,39 @@ class _MapWidgetState extends State<MapWidget> {
backgroundColor: Colors.blue, backgroundColor: Colors.blue,
child: Icon(Icons.navigation, color: Colors.white), child: Icon(Icons.navigation, color: Colors.white),
), ),
//if (locationController.getGpsSignalStrength() == 'low')
// child: Icon(Icons.warning, color: Colors.red),
], ],
), ),
/*
marker: DefaultLocationMarker(
child: Icon(
Icons.navigation,
color: Colors.yellowAccent,
),
),
*/
markerSize: Size(27, 27), markerSize: Size(27, 27),
markerDirection: MarkerDirection.heading, markerDirection: MarkerDirection.heading,
), ),
//child: const Icon(Icons.navigation),
), ),
indexController.locations.isNotEmpty && FutureBuilder<List<Marker>>(
indexController.locations[0].features.isNotEmpty future: indexController.locations.isNotEmpty ? _getMarkers() : null,
? MarkerLayer( builder: (context, snapshot) {
// マーカーの表示を担当 if (snapshot.connectionState == ConnectionState.waiting) {
markers: return const Center(child: CircularProgressIndicator());
indexController.locations[0].features.map((i) { } else if (snapshot.hasError) {
//print("i si ${i.properties!['location_id']}"); return const Center(child: Text('マーカーの読み込みに失敗しました'));
} else {
//RegExp regex = RegExp(r'([.]*0)(?!.*\d)'); return MarkerLayer(markers: snapshot.data ?? []);
GeoJSONMultiPoint p = }
i!.geometry as GeoJSONMultiPoint; },
//print( ),
// "lat is ${p.geoSerie!.geoPoints[0].latitude} and lon is ${p.geoSerie!.geoPoints[0].longitude}"); //MarkerLayer(markers: indexController.locations.isNotEmpty ? _getMarkers() : []),
return Marker(
alignment: Alignment.center,
height: 27.0,
width: 127.0,
point: LatLng(
p.coordinates[0][1], p.coordinates[0][0]),
child: getMarkerShape(i, context));
// マーカーのUIを生成しています。
// マーカーのアイコン、ラベル、色などをカスタマイズしています。
}).toList(),
)
: const Center(child: CircularProgressIndicator()),
], ],
)), )),
const Positioned(top: 0, left: 0, child: GameStateWidget()), const Positioned(top: 0, left: 0, child: GameStateWidget()),
// ゲーム状態の表示を担当。ゲームの状態(開始、終了など)を表示するカスタムウィジェットです。
const Positioned(bottom: 10, right: 10, child: CurrentPosition()), const Positioned(bottom: 10, right: 10, child: CurrentPosition()),
// 現在位置ボタンの表示を担当。現在位置に移動するためのボタンを表示するカスタムウィジェットです。
StreamBuilder<LocationMarkerPosition?>( StreamBuilder<LocationMarkerPosition?>(
stream: locationController.locationMarkerPositionStream, stream: locationController.locationMarkerPositionStream,
builder: (context, snapshot) { builder: (context, snapshot) {
if (snapshot.hasData) { if (!snapshot.hasData) {
print("Display current marker"); print("====== Not display current marker");
} else {
print("Not display current marker");
} }
return Container(); return Container();
}, },
) )
// const Positioned(
// bottom: 10,
// left: 0,
// child: DebugWidget(),
// )
], ],
); );
} }

View File

@ -0,0 +1,393 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_map/flutter_map.dart';
import 'package:flutter_map_location_marker/flutter_map_location_marker.dart';
import 'package:flutter_map_marker_cluster/flutter_map_marker_cluster.dart';
import 'package:flutter_polyline_points/flutter_polyline_points.dart';
import 'package:geojson_vi/geojson_vi.dart';
import 'package:get/get.dart';
import 'package:latlong2/latlong.dart';
import 'package:rogapp/model/destination.dart';
import 'package:rogapp/pages/destination/destination_controller.dart';
import 'package:rogapp/pages/index/index_controller.dart';
import 'package:rogapp/utils/database_helper.dart';
import 'package:rogapp/utils/location_controller.dart';
import 'package:rogapp/utils/text_util.dart';
import 'package:rogapp/widgets/base_layer_widget.dart';
import 'package:rogapp/widgets/bottom_sheet_new.dart';
import 'package:rogapp/widgets/current_position_widget.dart';
import 'package:rogapp/widgets/game_state_view.dart';
// map_widget.dartファイルは、アプリ内の地図表示を担当するウィジェットを定義しています。以下に、主要な部分を解説します。
// 地図表示に関連する主要な機能を提供しています。以下のような機能が含まれています。
//
// 地図の表示と操作
// マーカーの表示とカスタマイズ
// ルートの表示
// 現在位置の表示
// アイドル状態の処理
// ローディングインジケーターの表示
// ゲーム状態の表示
// 現在位置ボタンの表示
// StatefulWidgetを継承したクラスで、地図表示のメインウィジェットです。
//
class MapWidget extends StatefulWidget {
const MapWidget({Key? key}) : super(key: key);
@override
State<MapWidget> createState() => _MapWidgetState();
}
// MapWidgetの状態を管理するクラスです。
//
class _MapWidgetState extends State<MapWidget> {
final IndexController indexController = Get.find<IndexController>();
// IndexControllerのインスタンスを保持します。
final DestinationController destinationController =
Get.find<DestinationController>();
// DestinationControllerのインスタンスを保持します。
final LocationController locationController = Get.find<LocationController>();
// LocationControllerのインスタンスを保持します。
StreamSubscription? subscription; // 地図イベントの購読を保持します。
Timer? _timer; // アイドル状態のタイマーを保持します。
// 地図上のマーカーのUIを生成するメソッドです。
// GeoJSONFeatureを受け取り、マーカーのUIを返します。
//
Widget getMarkerShape(GeoJSONFeature i, BuildContext context) {
GeoJSONMultiPoint p = i.geometry as GeoJSONMultiPoint;
//print("lat is ${p.geoSerie!.geoPoints[0].latitude} and lon is ${p.geoSerie!.geoPoints[0].longitude}");
//RegExp regex = RegExp(r'([.]*0)(?!.*\d)');
return InkWell(
onTap: () {
GeoJSONFeature? fs = indexController.getFeatureForLatLong(
p.coordinates[0][1], p.coordinates[0][0]);
//print("------- fs $fs------");
if (fs != null) {
indexController.currentFeature.clear();
indexController.currentFeature.add(fs);
//print("----- fs is ${fs.properties!['photos']}");
Destination des = destinationController.festuretoDestination(fs);
DatabaseHelper db = DatabaseHelper.instance;
db.getDestinationByLatLon(des.lat!, des.lon!).then((value) {
destinationController.shouldShowBottomSheet = false;
showModalBottomSheet(
constraints:
BoxConstraints.loose(Size(Get.width, Get.height * 0.85)),
context: context,
isScrollControlled: true,
isDismissible: true,
builder: ((context) => BottomSheetNew(
destination: des, isAlreadyCheckedIn: value.isNotEmpty))
//builder:((context) => BottomSheetWidget())
).whenComplete(() {
destinationController.shouldShowBottomSheet = true;
destinationController.skipGps = false;
});
});
}
},
child: Stack(
fit: StackFit.expand,
//mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
height: 32,
width: 32,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Colors.transparent,
border: Border.all(
color: i.properties!['buy_point'] > 0
? Colors.blue
: Colors.red,
width: 3,
style: BorderStyle.solid)),
child: Stack(
alignment: Alignment.center,
children: [
const Icon(
Icons.circle,
size: 6.0,
),
i.properties!['cp'] == -1
? Transform.translate(
offset: const Offset(18, 0),
child: Transform.rotate(
alignment: Alignment.centerLeft,
origin: Offset.fromDirection(1, 26),
angle: 270 * pi / 180,
child: const Icon(
Icons.play_arrow_outlined,
color: Colors.red,
size: 70,
)),
)
: Container(
color: Colors.transparent,
),
],
)),
Transform.translate(
offset: const Offset(45, 0),
child: Align(
alignment: Alignment.center,
child: Container(
color: Colors.purple.withOpacity(0.2),
// child: Text(TextUtils.getDisplayTextFeture(i),
// style: const TextStyle(
// fontSize: 16,
// fontWeight: FontWeight.bold,
// color: Colors.red,
// ))),
child: Stack(
children: <Widget>[
// Text with white outline
Text(
TextUtils.getDisplayTextFeture(i),
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w700,
foreground: Paint()
..style = PaintingStyle.stroke
..strokeWidth = 1
..color = Colors.white,
),
),
// Text with black fill
Text(
TextUtils.getDisplayTextFeture(i),
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.w700,
color: Colors.black,
),
),
],
)),
),
)
],
),
);
}
// ルートポイントをLatLngのリストに変換するメソッドです。
// IndexControllerのroutePointsからLatLngのリストを生成しています。
//
List<LatLng>? getPoints() {
//print("##### --- route point ${indexController.routePoints.length}");
List<LatLng> pts = [];
for (PointLatLng p in indexController.routePoints) {
LatLng l = LatLng(p.latitude, p.longitude);
pts.add(l);
}
return pts;
}
// ウィジェットの初期化時に呼び出されるメソッドです。
// _startIdleTimerメソッドを呼び出して、アイドル状態のタイマーを開始します。
//
@override
void initState() {
super.initState();
_startIdleTimer(); // _startIdleTimerメソッドを呼び出してアイドル状態のタイマーを開始しています。
}
// アイドル状態のタイマーを開始するメソッドです。
// 一定時間後に_centerMapOnUserメソッドを呼び出すようにタイマーを設定しています。
//
void _startIdleTimer() {
_timer = Timer(const Duration(milliseconds: (1000 * 10)), _centerMapOnUser);
}
// アイドル状態のタイマーをリセットするメソッドです。
//
void _resetTimer() {
_timer?.cancel();
_startIdleTimer();
}
// 地図をユーザーの現在位置に中央揃えするメソッドです。
//
void _centerMapOnUser() {
assert(() {
print("showBottomSheet ${destinationController.shouldShowBottomSheet}");
return true;
}());
// 2024-04-03 Akira Log enabled only debug mode..
//
//if (destinationController.shouldShowBottomSheet) {
destinationController.centerMapToCurrentLocation();
//}
}
// ウィジェットのUIを構築するメソッドです。
// FlutterMapウィジェットを使用して地図を表示します。
// layersプロパティに、ベースレイヤー、ルートレイヤー、現在位置レイヤー、マーカーレイヤーを設定します。
// PopupControllerを使用して、ポップアップの制御を行います。
// IndexControllerのisLoading変数に基づいて、ローディングインジケーターを表示します。
// GameStateWidgetとCurrentPositionウィジェットを重ねて表示します。
//
@override
Widget build(BuildContext context) {
// print(
// "---------- rog mode is ${indexController.rog_mode.value.toString()}----------");
final PopupController popupController = PopupController();
return Stack(
children: [
// IndexControllerのisLoading変数に基づいて、ローディングインジケーターを表示します。
// isLoadingがtrueの場合はCircularProgressIndicatorを表示し、falseの場合は地図を表示します。
Obx(() => indexController.isLoading.value == true
? const Padding(
padding: EdgeInsets.only(top: 60.0),
child: CircularProgressIndicator(),
)
: FlutterMap(
// 地図の表示を担当
mapController: indexController.mapController,
options: MapOptions(
// 地図の初期設定(最大ズームレベル、初期位置、ズームレベルなど)を行っています。
maxZoom: 18.4,
onMapReady: () {
// print("Map controller ready!!"); ... working corretly
indexController.isMapControllerReady.value = true; // Added Akira,2024-4-6 for #2800
subscription = indexController.mapController.mapEventStream
.listen((MapEvent mapEvent) {
if (mapEvent is MapEventMoveStart) {
// print(DateTime.now().toString() +
// ' [MapEventMoveStart] START');
// do something
}
if (mapEvent is MapEventMoveEnd) {}
});
},
initialCenter: const LatLng(37.15319600454702, 139.58765950528198),
bounds: indexController.currentBound.isNotEmpty
? indexController.currentBound[0]
: LatLngBounds.fromPoints([
const LatLng(35.03999881162295, 136.40587119778962),
const LatLng(36.642756778706904, 137.95226720406063)
]),
initialZoom: 1,
interactiveFlags:
InteractiveFlag.pinchZoom | InteractiveFlag.drag,
onPositionChanged: (MapPosition pos, hasGesture) {
// 地図の位置が変更された際の処理を行います。
//print("map position changed ${pos.center!.latitude}");
if (hasGesture) {
_resetTimer();
}
indexController.currentBound = [pos.bounds!];
},
onTap: (_, __) => popupController
.hideAllPopups(), // Hide popup when the map is tapped.
),
children: [
const BaseLayer(),
Obx(
() => indexController.routePointLenght > 0
? PolylineLayer(
// ルートの表示を担当
polylines: [
Polyline(
points: getPoints()!, // ルートのポイントを設定しています。
strokeWidth: 6.0,
color: Colors.indigo),
],
)
: Container(),
),
CurrentLocationLayer(
// 現在位置の表示を担当
positionStream: locationController
.locationMarkerPositionStreamController.stream,
// locationMarkerPositionStreamController.streamを設定して、現在位置の更新を監視しています。
alignDirectionOnUpdate: AlignOnUpdate.never,
//turnOnHeadingUpdate: TurnOnHeadingUpdate.never,
style: const LocationMarkerStyle(
// styleプロパティで、現在位置のマーカーのスタイルを設定しています。
marker: Stack(
children: [
CircleAvatar(
radius: 13.5,
backgroundColor: Colors.blue,
child: Icon(Icons.navigation, color: Colors.white),
),
//if (locationController.getGpsSignalStrength() == 'low')
// child: Icon(Icons.warning, color: Colors.red),
],
),
/*
marker: DefaultLocationMarker(
child: Icon(
Icons.navigation,
color: Colors.yellowAccent,
),
),
*/
markerSize: Size(27, 27),
markerDirection: MarkerDirection.heading,
),
),
indexController.locations.isNotEmpty &&
indexController.locations[0].features.isNotEmpty
? MarkerLayer(
// マーカーの表示を担当
markers:
indexController.locations[0].features.map((i) {
//print("i si ${i.properties!['location_id']}");
//RegExp regex = RegExp(r'([.]*0)(?!.*\d)');
GeoJSONMultiPoint p =
i!.geometry as GeoJSONMultiPoint;
//print(
// "lat is ${p.geoSerie!.geoPoints[0].latitude} and lon is ${p.geoSerie!.geoPoints[0].longitude}");
return Marker(
alignment: Alignment.center,
height: 27.0,
width: 127.0,
point: LatLng(
p.coordinates[0][1], p.coordinates[0][0]),
child: getMarkerShape(i, context));
// マーカーのUIを生成しています。
// マーカーのアイコン、ラベル、色などをカスタマイズしています。
}).toList(),
)
: const Center(child: CircularProgressIndicator()),
],
)),
const Positioned(top: 0, left: 0, child: GameStateWidget()),
// ゲーム状態の表示を担当。ゲームの状態(開始、終了など)を表示するカスタムウィジェットです。
const Positioned(bottom: 10, right: 10, child: CurrentPosition()),
// 現在位置ボタンの表示を担当。現在位置に移動するためのボタンを表示するカスタムウィジェットです。
StreamBuilder<LocationMarkerPosition?>(
stream: locationController.locationMarkerPositionStream,
builder: (context, snapshot) {
if (snapshot.hasData) {
print("Display current marker");
} else {
print("Not display current marker");
}
return Container();
},
)
// const Positioned(
// bottom: 10,
// left: 0,
// child: DebugWidget(),
// )
],
);
}
}