2 Commits

29 changed files with 444 additions and 780 deletions

View File

@ -7,6 +7,8 @@ import 'package:get/get.dart';
//import 'package:vm_service/vm_service.dart'; //import 'package:vm_service/vm_service.dart';
//import 'package:dart_vm_info/dart_vm_info.dart'; //import 'package:dart_vm_info/dart_vm_info.dart';
import 'package:rogapp/pages/settings/settings_controller.dart';
import 'package:rogapp/pages/destination/destination_controller.dart'; import 'package:rogapp/pages/destination/destination_controller.dart';
import 'package:rogapp/pages/index/index_binding.dart'; import 'package:rogapp/pages/index/index_binding.dart';
import 'package:rogapp/routes/app_pages.dart'; import 'package:rogapp/routes/app_pages.dart';
@ -78,6 +80,7 @@ void main() async {
//Get.put(LocationController()); //Get.put(LocationController());
// startMemoryMonitoring(); // 2024-4-8 Akira: メモリ使用量のチェックを開始 See #2810 // startMemoryMonitoring(); // 2024-4-8 Akira: メモリ使用量のチェックを開始 See #2810
Get.put(SettingsController()); // これを追加
runZonedGuarded(() { runZonedGuarded(() {
runApp(const ProviderScope(child: MyApp())); runApp(const ProviderScope(child: MyApp()));

View File

@ -243,7 +243,10 @@ class CameraPage extends StatelessWidget {
destinationController.skipGps = false; destinationController.skipGps = false;
destinationController.isPhotoShoot.value = false; destinationController.isPhotoShoot.value = false;
Get.snackbar("チェックインしました。", Get.snackbar("チェックインしました。",
"${destination.sub_loc_id} : ${destination.name}"); "${destination.sub_loc_id} : ${destination.name}",
backgroundColor: Colors.green,
colorText: Colors.white
);
}, },
child: const Text("チェックイン")) child: const Text("チェックイン"))
: Container()) : Container())
@ -307,12 +310,18 @@ class CameraPage extends StatelessWidget {
if (value['status'] == 'OK') { if (value['status'] == 'OK') {
Get.back(); Get.back();
destinationController.skipGps = false; destinationController.skipGps = false;
Get.snackbar("目標が保存されました", "目標が正常に追加されました"); Get.snackbar("目標が保存されました", "目標が正常に追加されました",
backgroundColor: Colors.green,
colorText: Colors.white
);
destinationController.resetRogaining( destinationController.resetRogaining(
isgoal: true); isgoal: true);
} else { } else {
//print("---- status ${value['status']} ---- "); //print("---- status ${value['status']} ---- ");
Get.snackbar("目標が追加されていません", "please_try_again"); Get.snackbar("目標が追加されていません", "please_try_again",
backgroundColor: Colors.green,
colorText: Colors.white
);
} }
}); });
} on Exception catch (_) { } on Exception catch (_) {
@ -358,7 +367,10 @@ class CameraPage extends StatelessWidget {
destinationController.skipGps = false; destinationController.skipGps = false;
destinationController.isPhotoShoot.value = false; destinationController.isPhotoShoot.value = false;
Get.snackbar("お買い物加点を行いました。", Get.snackbar("お買い物加点を行いました。",
"${destination.sub_loc_id} : ${destination.name}"); "${destination.sub_loc_id} : ${destination.name}",
backgroundColor: Colors.green,
colorText: Colors.white
);
}, },
child: const Text("レシートの写真を撮ってください")) child: const Text("レシートの写真を撮ってください"))
: Container()) : Container())
@ -395,7 +407,10 @@ class CameraPage extends StatelessWidget {
destinationController.skipGps = false; destinationController.skipGps = false;
destinationController.isPhotoShoot.value = false; destinationController.isPhotoShoot.value = false;
Get.snackbar("お買い物加点を行いました。", Get.snackbar("お買い物加点を行いました。",
"${destination.sub_loc_id} : ${destination.name}"); "${destination.sub_loc_id} : ${destination.name}",
backgroundColor: Colors.green,
colorText: Colors.white
);
}, },
child: const Text("QRコードを読み取ってください")) child: const Text("QRコードを読み取ってください"))
: Container()) : Container())
@ -430,7 +445,10 @@ class CameraPage extends StatelessWidget {
Get.snackbar( Get.snackbar(
"チェックインしました", "チェックインしました",
indexController.currentDestinationFeature[0].name ?? indexController.currentDestinationFeature[0].name ??
""); "",
backgroundColor: Colors.green,
colorText: Colors.white
);
}, },
child: const Text("チェックイン")) child: const Text("チェックイン"))
: Container()) : Container())
@ -677,7 +695,10 @@ class BuyPointCamera extends StatelessWidget {
destinationController.skipGps = false; destinationController.skipGps = false;
destinationController.isPhotoShoot.value = false; destinationController.isPhotoShoot.value = false;
Get.snackbar("お買い物加点を行いました", Get.snackbar("お買い物加点を行いました",
"${destination.sub_loc_id} : ${destination.name}"); "${destination.sub_loc_id} : ${destination.name}",
backgroundColor: Colors.green,
colorText: Colors.white
);
}, },
child: const Text("完了")) child: const Text("完了"))
], ],

View File

@ -92,14 +92,16 @@ class ChangePasswordPage extends StatelessWidget {
Get.snackbar( Get.snackbar(
"no_values".tr, "no_values".tr,
"values_required".tr, "values_required".tr,
icon: const Icon( backgroundColor: Colors.red,
colorText: Colors.white,
icon: const Icon(
Icons.assistant_photo_outlined, Icons.assistant_photo_outlined,
size: 40.0, size: 40.0,
color: Colors.blue), color: Colors.blue),
snackPosition: SnackPosition.TOP, snackPosition: SnackPosition.TOP,
duration: const Duration( duration: const Duration(
milliseconds: 800), milliseconds: 800),
backgroundColor: Colors.yellow, //backgroundColor: Colors.yellow,
//icon:Image(image:AssetImage("assets/images/dora.png")) //icon:Image(image:AssetImage("assets/images/dora.png"))
); );
return; return;

View File

@ -33,6 +33,7 @@ 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:rogapp/utils/const.dart'; import 'package:rogapp/utils/const.dart';
import 'package:logger/logger.dart';
// 目的地に関連する状態管理とロジックを担当するクラスです。 // 目的地に関連する状態管理とロジックを担当するクラスです。
// //
@ -325,6 +326,16 @@ class DestinationController extends GetxController {
ds.isNotEmpty && ds[0].checkedin == true ? true : false; ds.isNotEmpty && ds[0].checkedin == true ? true : false;
bool isuserLoggedIn = indexController.currentUser.isNotEmpty ? true : false; // ログイン済みか bool isuserLoggedIn = indexController.currentUser.isNotEmpty ? true : false; // ログイン済みか
/*
// スタートとゴールは除外
debugPrint("startTimer CP=${d.cp}");
if (d.cp == -1 || d.cp == 0 || d.cp == -2) {
skipGps = false;
return;
}
*/
// 初期化。GPS信号が強くても弱くても // 初期化。GPS信号が強くても弱くても
if (checkinRadious >= distance || checkinRadious == -1) { if (checkinRadious >= distance || checkinRadious == -1) {
//currentSelectedDestinations.add(d); //currentSelectedDestinations.add(d);
@ -835,11 +846,13 @@ class DestinationController extends GetxController {
Get.snackbar( Get.snackbar(
"ロゲが始まっていません", "ロゲが始まっていません",
"ロゲ開始ボタンをタップして、ロゲイニングを始める必要があります", "ロゲ開始ボタンをタップして、ロゲイニングを始める必要があります",
icon: const Icon( backgroundColor: Colors.yellow,
colorText: Colors.white,
icon: const Icon(
Icons.assistant_photo_outlined, size: 40.0, color: Colors.blue), Icons.assistant_photo_outlined, size: 40.0, color: Colors.blue),
snackPosition: SnackPosition.TOP, snackPosition: SnackPosition.TOP,
duration: const Duration(seconds: 3), duration: const Duration(seconds: 3)
backgroundColor: Colors.yellow, // backgroundColor: Colors.yellow,
); );
} }
} }
@ -940,6 +953,8 @@ class DestinationController extends GetxController {
} }
} }
// ロゲイニングにデータを追加する関数です。 // ロゲイニングにデータを追加する関数です。
// //
void addToRogaining(double lat, double lon, int destinationId) async { void addToRogaining(double lat, double lon, int destinationId) async {
@ -966,13 +981,18 @@ class DestinationController extends GetxController {
// 指定されたパスの画像をギャラリーに保存する関数です。 // 指定されたパスの画像をギャラリーに保存する関数です。
// //
_saveImageFromPath(String imagePath) async { _saveImageFromPath(String imagePath) async {
// Read the image file from the given path try {
File imageFile = File(imagePath); // Read the image file from the given path
Uint8List imageBytes = await imageFile.readAsBytes(); File imageFile = File(imagePath);
Uint8List imageBytes = await imageFile.readAsBytes();
// Save the image to the gallery // Save the image to the gallery
final result = await ImageGallerySaver.saveImage(imageBytes); final result = await ImageGallerySaver.saveImage(imageBytes);
//print("--- save result --- ${result}"); //print("--- save result --- ${result}");
} catch(e, stackTrace){
print('エラーが発生しました: $e');
print('スタックトレース: $stackTrace');
}
} }
// 買い物ポイントを作成する関数です。 指定された目的地に対して買い物ポイントの処理を行います。 // 買い物ポイントを作成する関数です。 指定された目的地に対して買い物ポイントの処理を行います。
@ -1009,64 +1029,82 @@ class DestinationController extends GetxController {
} }
} }
// チェックインを行う関数です。 指定された目的地に対してチェックインの処理を行います。 // チェックインを行う関数です。 指定された目的地に対してチェックインの処理を行います。
// //
// 要検討:チェックインのリクエストが失敗した場合のエラーハンドリングを追加することをお勧めします。 // 要検討:チェックインのリクエストが失敗した場合のエラーハンドリングを追加することをお勧めします。
// //
Future<void> makeCheckin( Future<void> makeCheckin(
Destination destination, bool action, String imageurl) async { Destination destination, bool action, String imageurl) async {
// print("~~~~ calling checkin function ~~~~");
// print(
// "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ ressssss ${destination.sub_loc_id}@@@@@@@@@@@");
DatabaseHelper db = DatabaseHelper.instance;
List<Destination> ddd =
await db.getDestinationByLatLon(destination.lat!, destination.lon!);
if (ddd.isEmpty) { try {
destination.checkedin = true; // print("~~~~ calling checkin function ~~~~");
destination.checkin_image = imageurl; // print(
await db.insertDestination(destination); // "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ ressssss ${destination.sub_loc_id}@@@@@@@@@@@");
// print("~~~~ inserted into db ~~~~"); DatabaseHelper db = DatabaseHelper.instance;
} List<Destination> ddd =
await db.getDestinationByLatLon(destination.lat!, destination.lon!);
if (imageurl == null || imageurl.isEmpty) { if (ddd.isEmpty) {
if (photos.isNotEmpty) { destination.checkedin = true;
// imageurlが空の場合は、destinationcheckin_imageプロパティを使用する destination.checkin_image = imageurl;
debugPrint("photos = ${photos}"); await db.insertDestination(destination);
imageurl = photos[0].path; // print("~~~~ inserted into db ~~~~");
} }
debugPrint("imageurl = ${imageurl}");
await _saveImageFromPath(imageurl!); if (imageurl == null || imageurl.isEmpty) {
if (photos.isNotEmpty) {
// imageurlが空の場合は、destinationのcheckin_imageプロパティを使用する
debugPrint("photos = ${photos}");
imageurl = photos[0].path;
}
debugPrint("imageurl = ${imageurl}");
await _saveImageFromPath(imageurl!);
}
populateDestinations();
/// post to NATNAT
if (indexController.currentUser.isNotEmpty) {
double cpNum = destination.cp!;
int userId = indexController.currentUser[0]["user"]["id"];
//print("--- Pressed -----");
String team = indexController.currentUser[0]["user"]['team_name'];
//print("--- _team : ${_team}-----");
String eventCode = indexController.currentUser[0]["user"]["event_code"];
//print("--- _event_code : ${_event_code}-----");
String token = indexController.currentUser[0]["token"];
//print("--- _token : ${_token}-----");
DateTime now = DateTime.now();
String formattedDate = DateFormat('yyyy-MM-dd HH:mm:ss').format(now);
await addGPStoDB(currentLat, currentLon, isCheckin: 1);
// print("------ checkin event $eventCode ------");
ExternalService()
.makeCheckpoint(
userId,
token,
formattedDate,
team,
cpNum.round(),
eventCode,
imageurl)
.then((value) {
// print("------Ext service check point $value ------");
});
}
// dbService.updateDatabase();
}catch(e, stacktrace){
print("エラー:${e}");
//print("stack : ${stacktrace}");
}finally{
dbService.updateDatabase();
} }
populateDestinations();
/// post to NATNAT
if (indexController.currentUser.isNotEmpty) {
double cpNum = destination.cp!;
int userId = indexController.currentUser[0]["user"]["id"];
//print("--- Pressed -----");
String team = indexController.currentUser[0]["user"]['team_name'];
//print("--- _team : ${_team}-----");
String eventCode = indexController.currentUser[0]["user"]["event_code"];
//print("--- _event_code : ${_event_code}-----");
String token = indexController.currentUser[0]["token"];
//print("--- _token : ${_token}-----");
DateTime now = DateTime.now();
String formattedDate = DateFormat('yyyy-MM-dd HH:mm:ss').format(now);
await addGPStoDB(currentLat, currentLon, isCheckin: 1);
// print("------ checkin event $eventCode ------");
ExternalService()
.makeCheckpoint(userId, token, formattedDate, team, cpNum.round(),
eventCode, imageurl)
.then((value) {
// print("------Ext service check point $value ------");
});
}
dbService.updateDatabase();
} }
// チェックインを削除する関数です。 // チェックインを削除する関数です。
@ -1580,11 +1618,13 @@ class DestinationController extends GetxController {
Get.snackbar( Get.snackbar(
"画面切り替えでエラー", "画面切り替えでエラー",
"画面の切り替えができませんでした", "画面の切り替えができませんでした",
icon: const Icon( backgroundColor: Colors.red,
colorText: Colors.white,
icon: const Icon(
Icons.assistant_photo_outlined, size: 40.0, color: Colors.blue), Icons.assistant_photo_outlined, size: 40.0, color: Colors.blue),
snackPosition: SnackPosition.TOP, snackPosition: SnackPosition.TOP,
duration: const Duration(seconds: 3), duration: const Duration(seconds: 3),
backgroundColor: Colors.yellow, //backgroundColor: Colors.yellow,
); );
} }

View File

@ -4,6 +4,7 @@ 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/routes/app_pages.dart'; import 'package:rogapp/routes/app_pages.dart';
import 'package:rogapp/services/auth_service.dart'; import 'package:rogapp/services/auth_service.dart';
import 'package:rogapp/utils/database_helper.dart';
import 'package:url_launcher/url_launcher.dart'; import 'package:url_launcher/url_launcher.dart';
// SafeAreaウィジェットを使用して、画面の安全領域内にメニューを表示しています。 // SafeAreaウィジェットを使用して、画面の安全領域内にメニューを表示しています。
@ -127,12 +128,26 @@ class DrawerPage extends StatelessWidget {
textConfirm: "確認する", textConfirm: "確認する",
textCancel: "キャンセルする", textCancel: "キャンセルする",
onCancel: () => Get.back(), onCancel: () => Get.back(),
onConfirm: () { onConfirm: () async {
DestinationController destinationController = DestinationController destinationController =
Get.find<DestinationController>(); Get.find<DestinationController>();
DatabaseHelper databaseHelper = DatabaseHelper.instance;
// ゲーム中のデータを削除
await databaseHelper.deleteAllRogaining();
await databaseHelper.deleteAllDestinations();
destinationController.resetRogaining(); destinationController.resetRogaining();
destinationController.deleteDBDestinations();
//destinationController.resetRogaining();
//destinationController.deleteDBDestinations();
Get.back(); Get.back();
Get.snackbar(
"リセット完了",
"すべてリセットされました。ロゲ開始から再開して下さい。",
backgroundColor: Colors.green,
colorText: Colors.white,
duration: const Duration(seconds: 3),
);
}, },
); );
}, },
@ -161,7 +176,10 @@ class DrawerPage extends StatelessWidget {
indexController.logout(); indexController.logout();
Get.toNamed(AppPages.TRAVEL); Get.toNamed(AppPages.TRAVEL);
Get.snackbar("accounted_deleted".tr, Get.snackbar("accounted_deleted".tr,
"account_deleted_message".tr); "account_deleted_message".tr,
backgroundColor: Colors.green,
colorText: Colors.white
);
} }
}); });
}, },
@ -172,6 +190,7 @@ class DrawerPage extends StatelessWidget {
width: 0, width: 0,
height: 0, height: 0,
), ),
/*
// ユーザーデータ削除のアイテムは、ユーザーがログインしている場合にのみ表示されます。 // ユーザーデータ削除のアイテムは、ユーザーがログインしている場合にのみ表示されます。
// タップすると、AuthService.deleteUser()を呼び出してユーザーデータを削除します。 // タップすると、AuthService.deleteUser()を呼び出してユーザーデータを削除します。
indexController.currentUser.isNotEmpty indexController.currentUser.isNotEmpty
@ -215,7 +234,7 @@ class DrawerPage extends StatelessWidget {
// title: Text("point_rank".tr), // title: Text("point_rank".tr),
// onTap: (){}, // onTap: (){},
// ), // ),
*/
// "rog_web".trというテキストのアイテムは、ユーザーがログインしている場合にのみ表示されます。 // "rog_web".trというテキストのアイテムは、ユーザーがログインしている場合にのみ表示されます。
// タップすると、_launchURL()メソッドを呼び出して外部のウェブサイトを開きます。 // タップすると、_launchURL()メソッドを呼び出して外部のウェブサイトを開きます。
indexController.currentUser.isNotEmpty indexController.currentUser.isNotEmpty
@ -238,6 +257,15 @@ class DrawerPage extends StatelessWidget {
onTap: () { onTap: () {
_launchURL("https://rogaining.sumasen.net/api/privacy/"); _launchURL("https://rogaining.sumasen.net/api/privacy/");
}, },
),
ListTile(
leading: const Icon(Icons.settings),
title: const Text('設定'),
onTap: () {
Get.back(); // ドロワーを閉じる
Get.toNamed(Routes.SETTINGS);
},
) )
// ListTile( // ListTile(
// leading: const Icon(Icons.router), // leading: const Icon(Icons.router),

View File

@ -101,7 +101,7 @@ class _GpsPageState extends State<GpsPage> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: Text("GPS way points"), title: Text("移動履歴"),
), ),
body: Container( body: Container(
child: Obx( child: Obx(

View File

@ -205,10 +205,12 @@ class IndexController extends GetxController {
Get.snackbar( Get.snackbar(
"ログイン失敗", "ログイン失敗",
"ログインIDかパスワードを確認して下さい。", "ログインIDかパスワードを確認して下さい。",
icon: const Icon(Icons.error, size: 40.0, color: Colors.blue), backgroundColor: Colors.red,
colorText: Colors.white,
icon: const Icon(Icons.error, size: 40.0, color: Colors.blue),
snackPosition: SnackPosition.TOP, snackPosition: SnackPosition.TOP,
duration: const Duration(seconds: 3), duration: const Duration(seconds: 3),
backgroundColor: Colors.yellow, //backgroundColor: Colors.yellow,
//icon:Image(image:AssetImage("assets/images/dora.png")) //icon:Image(image:AssetImage("assets/images/dora.png"))
); );
} }
@ -235,10 +237,12 @@ class IndexController extends GetxController {
Get.snackbar( Get.snackbar(
'failed'.tr, 'failed'.tr,
'password_change_failed_please_try_again'.tr, 'password_change_failed_please_try_again'.tr,
icon: const Icon(Icons.error, size: 40.0, color: Colors.blue), backgroundColor: Colors.red,
colorText: Colors.white,
icon: const Icon(Icons.error, size: 40.0, color: Colors.blue),
snackPosition: SnackPosition.TOP, snackPosition: SnackPosition.TOP,
duration: const Duration(milliseconds: 800), duration: const Duration(milliseconds: 800),
backgroundColor: Colors.yellow, //backgroundColor: Colors.yellow,
//icon:Image(image:AssetImage("assets/images/dora.png")) //icon:Image(image:AssetImage("assets/images/dora.png"))
); );
} }
@ -277,10 +281,12 @@ class IndexController extends GetxController {
Get.snackbar( Get.snackbar(
'failed'.tr, 'failed'.tr,
'user_registration_failed_please_try_again'.tr, 'user_registration_failed_please_try_again'.tr,
icon: const Icon(Icons.error, size: 40.0, color: Colors.blue), backgroundColor: Colors.red,
colorText: Colors.white,
icon: const Icon(Icons.error, size: 40.0, color: Colors.blue),
snackPosition: SnackPosition.TOP, snackPosition: SnackPosition.TOP,
duration: const Duration(milliseconds: 800), duration: const Duration(milliseconds: 800),
backgroundColor: Colors.yellow, //backgroundColor: Colors.yellow,
//icon:Image(image:AssetImage("assets/images/dora.png")) //icon:Image(image:AssetImage("assets/images/dora.png"))
); );
} }
@ -455,11 +461,13 @@ class IndexController extends GetxController {
Get.snackbar( Get.snackbar(
"Too many Points", "Too many Points",
"please zoom in", "please zoom in",
icon: const Icon( backgroundColor: Colors.yellow,
colorText: Colors.white,
icon: const Icon(
Icons.assistant_photo_outlined, size: 40.0, color: Colors.blue), Icons.assistant_photo_outlined, size: 40.0, color: Colors.blue),
snackPosition: SnackPosition.TOP, snackPosition: SnackPosition.TOP,
duration: const Duration(seconds: 3), duration: const Duration(seconds: 3),
backgroundColor: Colors.yellow, //backgroundColor: Colors.yellow,
); );
showPopup = false; showPopup = false;
} }

View File

@ -107,7 +107,9 @@ class LoginPage extends StatelessWidget {
"no_values".tr, "no_values".tr,
"email_and_password_required" "email_and_password_required"
.tr, .tr,
icon: const Icon( backgroundColor: Colors.red,
colorText: Colors.white,
icon: const Icon(
Icons Icons
.assistant_photo_outlined, .assistant_photo_outlined,
size: 40.0, size: 40.0,
@ -116,7 +118,7 @@ class LoginPage extends StatelessWidget {
SnackPosition.TOP, SnackPosition.TOP,
duration: const Duration( duration: const Duration(
seconds: 3), seconds: 3),
backgroundColor: Colors.yellow, // backgroundColor: Colors.yellow,
//icon:Image(image:AssetImage("assets/images/dora.png")) //icon:Image(image:AssetImage("assets/images/dora.png"))
); );
return; return;
@ -262,10 +264,11 @@ class LoginPage extends StatelessWidget {
child: Padding( child: Padding(
padding: EdgeInsets.all(8.0), padding: EdgeInsets.all(8.0),
child: Text( child: Text(
"※第8回と第9回は、岐阜県からの「清流の国ぎふ」SDGs推進ネットワーク連携促進補助金を受けています", "※第8回と第9回は、岐阜県の令和5年度「清流の国ぎふ」SDGs推進ネットワーク連携促進補助金を受けています",
style: TextStyle( style: TextStyle(
//overflow: TextOverflow.ellipsis,
fontSize: fontSize:
12, // Consider adjusting the font size if the text is too small. 10.0, // Consider adjusting the font size if the text is too small.
// Removed overflow: TextOverflow.ellipsis to allow text wrapping. // Removed overflow: TextOverflow.ellipsis to allow text wrapping.
), ),
), ),

View File

@ -100,6 +100,8 @@ class LoginPopupPage extends StatelessWidget {
"no_values".tr, "no_values".tr,
"email_and_password_required" "email_and_password_required"
.tr, .tr,
backgroundColor: Colors.red,
colorText: Colors.white,
icon: const Icon( icon: const Icon(
Icons Icons
.assistant_photo_outlined, .assistant_photo_outlined,
@ -109,7 +111,7 @@ class LoginPopupPage extends StatelessWidget {
SnackPosition.TOP, SnackPosition.TOP,
duration: const Duration( duration: const Duration(
milliseconds: 800), milliseconds: 800),
backgroundColor: Colors.yellow, //backgroundColor: Colors.yellow,
//icon:Image(image:AssetImage("assets/images/dora.png")) //icon:Image(image:AssetImage("assets/images/dora.png"))
); );
return; return;

View File

@ -101,11 +101,13 @@ class RegisterPage extends StatelessWidget {
Get.snackbar( Get.snackbar(
"No match", "No match",
"Passwords does not match", "Passwords does not match",
icon: const Icon(Icons.assistant_photo_outlined, backgroundColor: Colors.red,
colorText: Colors.white,
icon: const Icon(Icons.assistant_photo_outlined,
size: 40.0, color: Colors.blue), size: 40.0, color: Colors.blue),
snackPosition: SnackPosition.TOP, snackPosition: SnackPosition.TOP,
duration: const Duration(milliseconds: 800), duration: const Duration(milliseconds: 800),
backgroundColor: Colors.yellow, // backgroundColor: Colors.yellow,
//icon:Image(image:AssetImage("assets/images/dora.png")) //icon:Image(image:AssetImage("assets/images/dora.png"))
); );
} }
@ -114,11 +116,13 @@ class RegisterPage extends StatelessWidget {
Get.snackbar( Get.snackbar(
"no_values".tr, "no_values".tr,
"email_and_password_required".tr, "email_and_password_required".tr,
icon: const Icon(Icons.assistant_photo_outlined, backgroundColor: Colors.red,
colorText: Colors.white,
icon: const Icon(Icons.assistant_photo_outlined,
size: 40.0, color: Colors.blue), size: 40.0, color: Colors.blue),
snackPosition: SnackPosition.TOP, snackPosition: SnackPosition.TOP,
duration: const Duration(milliseconds: 800), duration: const Duration(milliseconds: 800),
backgroundColor: Colors.yellow, //backgroundColor: Colors.yellow,
//icon:Image(image:AssetImage("assets/images/dora.png")) //icon:Image(image:AssetImage("assets/images/dora.png"))
); );
return; return;

View File

@ -0,0 +1,12 @@
// lib/pages/settings/settings_binding.dart
import 'package:get/get.dart';
import 'package:rogapp/pages/settings/settings_controller.dart';
class SettingsBinding extends Bindings {
@override
void dependencies() {
Get.put<SettingsController>(SettingsController()); // これを修正
//Get.lazyPut<SettingsController>(() => SettingsController());
}
}

View File

@ -0,0 +1,26 @@
// lib/pages/settings/settings_controller.dart
import 'package:get/get.dart';
import 'package:rogapp/widgets/map_widget.dart';
class SettingsController extends GetxController {
var timerDuration = Duration(seconds: 10).obs;
var autoReturnDisabled = false.obs;
final MapResetController mapResetController = Get.put(MapResetController());
void updateTimerDuration(int seconds) {
timerDuration.value = Duration(seconds: seconds);
}
void setAutoReturnDisabled(bool value) {
autoReturnDisabled.value = value;
if (!value) {
resetIdleTimer();
}
}
void resetIdleTimer() {
mapResetController.resetIdleTimer!();
}
}

View File

@ -0,0 +1,63 @@
// lib/pages/settings/settings_page.dart
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:rogapp/pages/settings/settings_controller.dart';
class SettingsPage extends GetView<SettingsController> {
const SettingsPage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('設定'),
),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'タイマーの長さ',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 8),
Obx(
() => controller.autoReturnDisabled.value
? Container()
: Slider(
value: controller.timerDuration.value.inSeconds.toDouble(),
min: 5,
max: 30,
divisions: 5,
label: '${controller.timerDuration.value.inSeconds}',
onChanged: (value) {
controller.updateTimerDuration(value.toInt());
},
),
),
const SizedBox(height: 8),
const Text(
'マップ操作がなければ自動的に現在地に復帰します。そのタイマー秒数を入れて下さい。チェックボックスをチェックすると、自動復帰は行われなくなります。',
style: TextStyle(fontSize: 14),
),
const SizedBox(height: 16),
Obx(
() => CheckboxListTile(
title: const Text('自動復帰なし'),
value: controller.autoReturnDisabled.value,
onChanged: (value) {
controller.setAutoReturnDisabled(value!);
},
),
),
],
),
),
);
}
}

View File

@ -19,6 +19,8 @@ import 'package:rogapp/pages/progress/progress.dart';
import 'package:rogapp/pages/register/register_page.dart'; import 'package:rogapp/pages/register/register_page.dart';
import 'package:rogapp/pages/search/search_binding.dart'; import 'package:rogapp/pages/search/search_binding.dart';
import 'package:rogapp/pages/search/search_page.dart'; import 'package:rogapp/pages/search/search_page.dart';
import 'package:rogapp/pages/settings/settings_page.dart';
import 'package:rogapp/pages/settings/settings_binding.dart';
import 'package:rogapp/pages/subperf/subperf_page.dart'; import 'package:rogapp/pages/subperf/subperf_page.dart';
import 'package:rogapp/spa/spa_binding.dart'; import 'package:rogapp/spa/spa_binding.dart';
import 'package:rogapp/spa/spa_page.dart'; import 'package:rogapp/spa/spa_page.dart';
@ -48,6 +50,7 @@ class AppPages {
static const PROGRESS = Routes.PROGRESS; static const PROGRESS = Routes.PROGRESS;
static const HISTORY = Routes.HISTORY; static const HISTORY = Routes.HISTORY;
static const GPS = Routes.GPS; static const GPS = Routes.GPS;
static const SETTINGS = Routes.SETTINGS;
static final routes = [ static final routes = [
GetPage( GetPage(
@ -108,6 +111,11 @@ class AppPages {
GetPage( GetPage(
name: Routes.GPS, name: Routes.GPS,
page: () => const GpsPage(), page: () => const GpsPage(),
),
GetPage(
name: Routes.SETTINGS,
page: () => const SettingsPage(),
binding: SettingsBinding(),
) )
]; ];
} }

View File

@ -26,4 +26,5 @@ abstract class Routes {
static const PROGRESS = '/progress'; static const PROGRESS = '/progress';
static const HISTORY = '/history'; static const HISTORY = '/history';
static const GPS = '/gp'; static const GPS = '/gp';
static const SETTINGS = '/settings';
} }

View File

@ -110,15 +110,19 @@ class AuthService {
} }
} catch( e ){ } catch( e ){
print('Error in login: $e'); print('Error in login: $e');
Get.snackbar("通信エラーがおきました", "サーバーと通信できませんでした"); Get.snackbar("通信エラーがおきました", "サーバーと通信できませんでした",
backgroundColor: Colors.red,
colorText: Colors.white);
Get.snackbar( Get.snackbar(
"通信エラーがおきました", "通信エラーがおきました",
"サーバーと通信できませんでした", "サーバーと通信できませんでした",
backgroundColor: Colors.red,
colorText: Colors.white,
icon: const Icon( icon: const Icon(
Icons.assistant_photo_outlined, size: 40.0, color: Colors.blue), Icons.assistant_photo_outlined, size: 40.0, color: Colors.blue),
snackPosition: SnackPosition.TOP, snackPosition: SnackPosition.TOP,
duration: const Duration(seconds: 3), duration: const Duration(seconds: 3),
backgroundColor: Colors.yellow, //backgroundColor: Colors.yellow,
); );
cats = {}; cats = {};
} }

View File

@ -1,4 +1,5 @@
import 'dart:io'; import 'dart:io';
import 'package:flutter/material.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:http/http.dart' as http; import 'package:http/http.dart' as http;
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
@ -32,6 +33,8 @@ class ExternalService {
Future<Map<String, dynamic>> startRogaining() async { Future<Map<String, dynamic>> startRogaining() async {
final IndexController indexController = Get.find<IndexController>(); final IndexController indexController = Get.find<IndexController>();
debugPrint("== startRogaining ==");
Map<String, dynamic> res = {}; Map<String, dynamic> res = {};
int userId = indexController.currentUser[0]["user"]["id"]; int userId = indexController.currentUser[0]["user"]["id"];
@ -42,6 +45,7 @@ class ExternalService {
if (indexController.connectionStatusName.value != "wifi" && if (indexController.connectionStatusName.value != "wifi" &&
indexController.connectionStatusName.value != "mobile") { indexController.connectionStatusName.value != "mobile") {
debugPrint("== No network ==");
DatabaseHelper db = DatabaseHelper.instance; DatabaseHelper db = DatabaseHelper.instance;
Rog rog = Rog( Rog rog = Rog(
id: 1, id: 1,
@ -54,6 +58,8 @@ class ExternalService {
rog_action_type: 0); rog_action_type: 0);
db.insertRogaining(rog); db.insertRogaining(rog);
} else { } else {
debugPrint("== startRogaining processing==");
String url = 'https://rogaining.sumasen.net/gifuroge/start_from_rogapp'; String url = 'https://rogaining.sumasen.net/gifuroge/start_from_rogapp';
//print('++++++++$url'); //print('++++++++$url');
final http.Response response = await http.post( final http.Response response = await http.post(
@ -94,6 +100,8 @@ class ExternalService {
if (imageurl != null) { if (imageurl != null) {
if (indexController.connectionStatusName.value != "wifi" && if (indexController.connectionStatusName.value != "wifi" &&
indexController.connectionStatusName.value != "mobile") { indexController.connectionStatusName.value != "mobile") {
debugPrint("== checkin without network ==");
DatabaseHelper db = DatabaseHelper.instance; DatabaseHelper db = DatabaseHelper.instance;
Rog rog = Rog( Rog rog = Rog(
id: 1, id: 1,
@ -107,6 +115,7 @@ class ExternalService {
); );
db.insertRogaining(rog); db.insertRogaining(rog);
} else { } else {
debugPrint("== Normal Check in ===");
String serverUrl = ConstValues.currentServer(); String serverUrl = ConstValues.currentServer();
String url1 = "$serverUrl/api/checkinimage/"; String url1 = "$serverUrl/api/checkinimage/";
final im1Bytes = File(imageurl).readAsBytesSync(); final im1Bytes = File(imageurl).readAsBytesSync();
@ -167,16 +176,26 @@ class ExternalService {
if (response2.statusCode == 200) { if (response2.statusCode == 200) {
res = json.decode(utf8.decode(response2.bodyBytes)); res = json.decode(utf8.decode(response2.bodyBytes));
//print('----checkin res _res : $res ----'); //print('----checkin res _res : $res ----');
if (res["status"] == "ERROR") { if (res["status"] == "ERROR" && cp>0 ) {
Get.snackbar("エラーがおきました", res["detail"]); // スタート・ゴールはエラー除外。
Get.snackbar("エラーがおきました", res["detail"],
backgroundColor: Colors.red,
colorText: Colors.white
);
} }
} }
} else { } else {
Get.snackbar("サーバーエラーがおきました", "サーバーと通信できませんでした"); Get.snackbar("サーバーエラーがおきました", "サーバーと通信できませんでした",
backgroundColor: Colors.red,
colorText: Colors.white
);
} }
} catch( e ) { } catch( e ) {
print('Error in makeCheckpoint: $e'); print('Error in makeCheckpoint: $e');
Get.snackbar("通信エラーがおきました", "サーバーと通信できませんでした"); Get.snackbar("通信エラーがおきました", "サーバーと通信できませんでした",
backgroundColor: Colors.red,
colorText: Colors.white
);
} }
} }
} else { } else {
@ -234,6 +253,8 @@ class ExternalService {
final DestinationController destinationController = final DestinationController destinationController =
Get.find<DestinationController>(); Get.find<DestinationController>();
debugPrint("== goal Rogaining ==");
//if(indexController.connectionStatusName != "wifi" && indexController.connectionStatusName != "mobile"){ //if(indexController.connectionStatusName != "wifi" && indexController.connectionStatusName != "mobile"){
DatabaseHelper db = DatabaseHelper.instance; DatabaseHelper db = DatabaseHelper.instance;
Rog rog = Rog( Rog rog = Rog(

View File

@ -79,7 +79,7 @@ class StringValues extends Translations{
'drawer_title':'ロゲイニング参加者はログイン するとチェックポイントが参照 できます', 'drawer_title':'ロゲイニング参加者はログイン するとチェックポイントが参照 できます',
'app_title': '旅行工程表', 'app_title': '旅行工程表',
'address':'住所', 'address':'住所',
'email':'Eメール', 'email':'ゼッケン番号',
'password':'パスワード', 'password':'パスワード',
'web':'ウェブ', 'web':'ウェブ',
'wikipedia':'ウィキペディア', 'wikipedia':'ウィキペディア',
@ -147,7 +147,7 @@ class StringValues extends Translations{
"accounted_deleted": "アカウントが削除されました", "accounted_deleted": "アカウントが削除されました",
"account_deleted_message": "あなたのアカウントは正常に削除されました", "account_deleted_message": "あなたのアカウントは正常に削除されました",
"privacy": "プライバシーポリシー", "privacy": "プライバシーポリシー",
"app_developed_by_gifu_dx": "このアプリは岐阜県DX補助金事業で開発されました。" "app_developed_by_gifu_dx": "このアプリは令和4年度岐阜県DX補助金事業で開発されました。"
}, },
}; };
} }

View File

@ -15,7 +15,7 @@ class GameStatusIndicator extends StatelessWidget {
IconData iconData = IconData iconData =
gameStarted ? Icons.stop_circle : Icons.play_circle_filled; gameStarted ? Icons.stop_circle : Icons.play_circle_filled;
// Text to show based on the game status // Text to show based on the game status
String text = gameStarted ? 'ゲーム開始' : 'ゲーム未開始'; String text = gameStarted ? 'ゲーム' : 'ゲーム';
// Layout for minimized view // Layout for minimized view
if (minimized) { if (minimized) {

View File

@ -198,6 +198,7 @@ class BottomSheetNew extends GetView<BottomSheetController> {
// Clear data and start game logic here // Clear data and start game logic here
destinationController.isInRog.value = true; destinationController.isInRog.value = true;
destinationController.resetRogaining(); destinationController.resetRogaining();
destinationController.addToRogaining( destinationController.addToRogaining(
destinationController.currentLat, destinationController.currentLat,
destinationController.currentLon, destinationController.currentLon,
@ -480,16 +481,37 @@ class BottomSheetNew extends GetView<BottomSheetController> {
// Finish or Goal // Finish or Goal
getActionButton(context, destination), getActionButton(context, destination),
//remove checkin //remove checkin
isAlreadyCheckedIn == true && destination.cp != -1 isAlreadyCheckedIn == true && destination.cp != 0 && destination.cp != -1 && destination.cp != -2
? ElevatedButton( ? ElevatedButton(
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
backgroundColor: Colors.blueAccent), backgroundColor: Colors.blueAccent),
onPressed: () async { onPressed: () async {
await destinationController try {
.removeCheckin(destination.cp!.toInt()); await destinationController
destinationController .removeCheckin(destination.cp!.toInt());
.deleteDestination(destination); destinationController
Get.back(); .deleteDestination(destination);
Get.back();
Get.snackbar(
'チェックイン取り消し',
'${destination
.name}のチェックインは取り消されました',
backgroundColor: Colors.green,
colorText: Colors.white,
duration: Duration(seconds: 3),
);
} catch (e ) {
// エラーハンドリング
Get.snackbar(
'Error',
'An error occurred while canceling check-in.',
backgroundColor: Colors.red,
colorText: Colors.white,
duration: Duration(seconds: 3),
);
// 必要に応じてエラーログを記録
print('Error canceling check-in: $e');
}
}, },
child: const Text( child: const Text(
"チェックイン取消", "チェックイン取消",

View File

@ -1,325 +0,0 @@
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

@ -1,34 +0,0 @@
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

@ -1,69 +0,0 @@
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

@ -1,64 +0,0 @@
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

@ -1,102 +0,0 @@
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

@ -1,43 +0,0 @@
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

@ -1,6 +1,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:rogapp/pages/destination/destination_controller.dart'; import 'package:rogapp/pages/destination/destination_controller.dart';
import 'package:rogapp/routes/app_pages.dart'; // これを追加
class CurrentPosition extends StatefulWidget { class CurrentPosition extends StatefulWidget {
const CurrentPosition({super.key}); const CurrentPosition({super.key});
@ -13,20 +14,28 @@ class _CurrentPositionState extends State<CurrentPosition> {
final DestinationController destinationController = final DestinationController destinationController =
Get.find<DestinationController>(); Get.find<DestinationController>();
void _onLongPress() {
Get.toNamed(AppPages.SETTINGS); // これを追加
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Container( return GestureDetector( // GestureDetectorを追加
width: 40, onLongPress: _onLongPress, // 長押しイベントを追加
height: 40, child: Container (
decoration: BoxDecoration( // return Container(
color: Colors.grey, borderRadius: BorderRadius.circular(20.0)), width: 40,
child: IconButton( height: 40,
onPressed: () { decoration: BoxDecoration(
destinationController.centerMapToCurrentLocation(); color: Colors.grey, borderRadius: BorderRadius.circular(20.0)),
}, child: IconButton(
icon: const Icon( onPressed: () {
Icons.location_searching, destinationController.centerMapToCurrentLocation();
color: Colors.white, },
icon: const Icon(
Icons.location_searching,
color: Colors.white,
),
), ),
), ),
); );

View File

@ -127,7 +127,9 @@ class DestinationWidget extends StatelessWidget {
destinationController.deleteAllDestinations(); destinationController.deleteAllDestinations();
Get.back(); Get.back();
Get.snackbar( Get.snackbar(
"deleted".tr, "all_destinations_are_deleted_successfully".tr); "deleted".tr, "all_destinations_are_deleted_successfully".tr,
backgroundColor: Colors.green,
colorText: Colors.white);
}); });
} }

View File

@ -6,6 +6,7 @@ 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';
import 'package:latlong2/latlong.dart'; import 'package:latlong2/latlong.dart';
import 'package:rogapp/pages/settings/settings_binding.dart';
import 'package:rogapp/model/destination.dart'; 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';
@ -16,8 +17,13 @@ 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:rogapp/pages/settings/settings_controller.dart';
import 'package:flutter_map_tile_caching/flutter_map_tile_caching.dart'; import 'package:flutter_map_tile_caching/flutter_map_tile_caching.dart';
class MapResetController {
void Function()? resetIdleTimer;
}
class MapWidget extends StatefulWidget { class MapWidget extends StatefulWidget {
const MapWidget({Key? key}) : super(key: key); const MapWidget({Key? key}) : super(key: key);
@ -31,6 +37,7 @@ class _MapWidgetState extends State<MapWidget> with WidgetsBindingObserver {
final DestinationController destinationController = final DestinationController destinationController =
Get.find<DestinationController>(); Get.find<DestinationController>();
final LocationController locationController = Get.find<LocationController>(); final LocationController locationController = Get.find<LocationController>();
final SettingsController settingsController = Get.find<SettingsController>();
late MapController mapController; late MapController mapController;
final Completer<MapController> mapControllerCompleter = Completer<MapController>(); final Completer<MapController> mapControllerCompleter = Completer<MapController>();
@ -45,6 +52,7 @@ class _MapWidgetState extends State<MapWidget> with WidgetsBindingObserver {
@override @override
void initState() { void initState() {
super.initState(); super.initState();
SettingsBinding().dependencies(); // これを追加
_startIdleTimer(); _startIdleTimer();
mapController = MapController(); mapController = MapController();
indexController.mapController = mapController; indexController.mapController = mapController;
@ -67,10 +75,19 @@ class _MapWidgetState extends State<MapWidget> with WidgetsBindingObserver {
}); });
}); });
late MapResetController mapResetController = MapResetController();
mapResetController.resetIdleTimer = _resetIdleTimer;
Get.put(mapResetController);
// indexController.mapController = MapController(initCompleter: mapControllerCompleter); // indexController.mapController = MapController(initCompleter: mapControllerCompleter);
} }
void _resetIdleTimer() {
_timer?.cancel();
_startIdleTimer();
}
@override @override
void dispose() { void dispose() {
WidgetsBinding.instance.removeObserver(this); // added WidgetsBinding.instance.removeObserver(this); // added
@ -90,7 +107,10 @@ class _MapWidgetState extends State<MapWidget> with WidgetsBindingObserver {
// _centerMapOnUser を10秒間でコール // _centerMapOnUser を10秒間でコール
void _startIdleTimer() { void _startIdleTimer() {
_timer = Timer(const Duration(milliseconds: (1000 * 10)), _centerMapOnUser); final settingsController = Get.find<SettingsController>();
if (!settingsController.autoReturnDisabled.value) {
_timer = Timer(settingsController.timerDuration.value, _centerMapOnUser);
}
} }
// タイマーをリセットして_startIdleTimer をコール // タイマーをリセットして_startIdleTimer をコール
@ -174,6 +194,7 @@ class _MapWidgetState extends State<MapWidget> with WidgetsBindingObserver {
// Widget getMarkerShape(GeoJSONFeature i, BuildContext context) { // Widget getMarkerShape(GeoJSONFeature i, BuildContext context) {
Widget getMarkerShape(GeoJSONFeature i) { Widget getMarkerShape(GeoJSONFeature i) {
/*
String labelText = TextUtils.getDisplayTextFeture(i); String labelText = TextUtils.getDisplayTextFeture(i);
if( i.properties!['cp'] == 0 ){ if( i.properties!['cp'] == 0 ){
labelText="Start/Goal"; labelText="Start/Goal";
@ -184,10 +205,11 @@ class _MapWidgetState extends State<MapWidget> with WidgetsBindingObserver {
//}else{ //}else{
// labelText=i.properties!['cp']; // labelText=i.properties!['cp'];
} }
*/
//if( i.properties!['cp'] <= 0 ) { //if( i.properties!['cp'] <= 0 ) {
// debugPrint("Text=${labelText}"); // debugPrint("Text=${labelText}");
//} //}
final double maxWidth = labelText.length * 16.0; //16.0; //final double maxWidth = labelText.length * 16.0; //16.0;
GeoJSONMultiPoint p = i.geometry as GeoJSONMultiPoint; GeoJSONMultiPoint p = i.geometry as GeoJSONMultiPoint;
return InkWell( return InkWell(
onTap: () { onTap: () {
@ -238,21 +260,20 @@ class _MapWidgetState extends State<MapWidget> with WidgetsBindingObserver {
Icons.circle, Icons.circle,
size: 6.0, size: 6.0,
), ),
i.properties!['cp'] <= 0 i.properties!['cp'] <= 0 ? Transform.translate
? Transform.translate
( (
offset: const Offset(-3, 0), offset: const Offset(-3, 0), //-3
child: Transform.rotate( child: Transform.rotate(
alignment: Alignment.centerLeft, alignment: Alignment.centerLeft,
origin: Offset.fromDirection(1, 26), origin: Offset.fromDirection(1, 26),
angle: 270 * pi / 180, angle: 270 * pi / 180,
child: const Icon( child: const Icon(
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,
), ),
@ -262,42 +283,42 @@ class _MapWidgetState extends State<MapWidget> with WidgetsBindingObserver {
Transform.translate( Transform.translate(
offset: const Offset(30, 0), // 30,0 offset: const Offset(30, 0), // 30,0
child: Align( child: Align(
alignment: Alignment.centerLeft, alignment: Alignment.center,
child: Container ( child: Container (
//width: 200, // 幅を指定 //width: 200, // 幅を指定
//height: 60, // 40 //height: 60, // 40
color: Colors.transparent, color: Colors.transparent,
//child: Text(' '). //child: Text(' ').
//constraints: const BoxConstraints(maxWidth: 1000.0), // 最大幅を設定 //constraints: const BoxConstraints(maxWidth: 1000.0), // 最大幅を設定
constraints: BoxConstraints(maxWidth: maxWidth), // 最大幅を設定 //constraints: BoxConstraints(maxWidth: maxWidth), // 最大幅を設定
//color: Colors.purple.withOpacity(0.2), //color: Colors.purple.withOpacity(0.2),
child: Stack( child: Stack(
children: <Widget>[ children: <Widget>[
Text( // アウトライン Text( // アウトライン
labelText, TextUtils.getDisplayTextFeture(i),
style: TextStyle( style: TextStyle(
fontSize: 14, // 16 fontSize: 16, // 16
fontWeight: FontWeight.w700, fontWeight: FontWeight.w700,
height: 1.2, //height: 1.2,
foreground: Paint() foreground: Paint()
..style = PaintingStyle.stroke ..style = PaintingStyle.stroke
..strokeWidth = 2 ..strokeWidth = 2
..color = Colors.white, ..color = Colors.white,
), ),
maxLines: 1, // テキストを1行に制限 //maxLines: 1, // テキストを1行に制限
softWrap: false, // テキストの折り返しを無効化 //softWrap: false, // テキストの折り返しを無効化
), ),
Text( // テキスト Text( // テキスト
labelText, TextUtils.getDisplayTextFeture(i),
style: const TextStyle( style: const TextStyle(
fontSize: 14, fontSize: 16,
fontWeight: FontWeight.w700, fontWeight: FontWeight.w700,
//fontWeight: FontWeight.bold, //fontWeight: FontWeight.bold,
height: 1.2, //height: 1.2,
color: Colors.black, color: Colors.black,
), ),
maxLines: 1, // テキストを1行に制限 //maxLines: 1, // テキストを1行に制限
softWrap: false, // テキストの折り返しを無効化 //softWrap: false, // テキストの折り返しを無効化
), ),
], ],
), ),
@ -321,6 +342,7 @@ class _MapWidgetState extends State<MapWidget> with WidgetsBindingObserver {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final settingsController = Get.find<SettingsController>(); // これを追加
//final PopupController popupController = PopupController(); //final PopupController popupController = PopupController();
return Stack( return Stack(
children: [ children: [