未確認だが問題回避のためプッシュ

This commit is contained in:
2024-04-09 01:58:25 +09:00
parent dd9dd0d087
commit 1b4073f690
59 changed files with 2384 additions and 410 deletions

View File

@ -31,6 +31,8 @@ String getTagText(bool isRecept, String? tags) {
return "";
}
// 要修正:画像の読み込みエラーが発生した場合のエラーハンドリングが不十分です。エラーメッセージを表示するなどの処理を追加してください。
//
Image getDisplayImage(Destination destination) {
String serverUrl = ConstValues.currentServer();
@ -104,6 +106,8 @@ class CameraPage extends StatelessWidget {
Timer? timer;
// 要修正:エラーハンドリングが不十分です。例外が発生した場合の処理を追加することをお勧めします。
//
Widget getAction(BuildContext context) {
//print("----cccheckin is --- ${dbDest?.checkedin} ----");
if (manulaCheckin == true) {
@ -431,6 +435,8 @@ class BuyPointCamera extends StatelessWidget {
height: 370,
decoration: BoxDecoration(
image: DecorationImage(
// 要修正getReceiptImage関数の戻り値がnullの場合のエラーハンドリングが不十分です。適切なデフォルト画像を表示するなどの処理を追加してください。
//
image: getReceiptImage(), fit: BoxFit.cover)),
),
),

View File

@ -33,6 +33,8 @@ import 'package:shared_preferences/shared_preferences.dart';
import 'package:image_gallery_saver/image_gallery_saver.dart';
import 'package:geolocator/geolocator.dart';
// 目的地に関連する状態管理とロジックを担当するクラスです。
//
class DestinationController extends GetxController {
@ -44,6 +46,8 @@ class DestinationController extends GetxController {
List<Destination> destinations = <Destination>[].obs; // 目的地のリストを保持するObservable変数です。
double currentLat = 0.0; // 現在の緯度と経度を保持する変数です。
double currentLon = 0.0;
double lastValidLat = 0.0; // 最後に中・強信号で拾ったGPS位置。ロゲ開始を屋内でやったら 0 のままなので、屋外で行うこと。
double lastValidLon = 0.0;
DateTime lastGPSCollectedTime = DateTime.now(); // 最後にGPSデータが収集された時刻を保持する変数です。
bool shouldShowBottomSheet = true; // ボトムシートを表示すべきかどうかを示すフラグです。
@ -75,6 +79,7 @@ class DestinationController extends GetxController {
var travelMode = 0.obs; // 移動モードを保持するReactive変数です。
bool skipGps = false; // GPSをスキップするかどうかを示すフラグです。
bool okToUseGPS = false; // 最新のGPS情報を使用して良いかを示すフラグ。
Map<String, dynamic> matrix = {}; // 行列データを保持する変数です。
@ -199,6 +204,10 @@ class DestinationController extends GetxController {
// 目的地がデータベースに存在しない場合は、新しい目的地としてデータベースに挿入します。
// 目的地に応じて、チェックイン、ゴール、買い物ポイントの処理を行います。
//
// 2024-4-8 akira: GPS信号が弱い場合でも、最後に取得した位置情報を使用してチェックインやゴールの処理を続行できるようになります。また、チェックインやゴールの処理では、GPS信号の精度チェックを緩和することで、GPS信号が弱い場合でもボタンを押せるようになります。
//
// 要検討:エラーが発生した場合のエラーハンドリングを追加し、適切なメッセージを表示することを検討してください。
//
Future<void> startTimer(Destination d, double distance) async {
//print("=== passed dest is ${d.location_id} ${d.checkedin} ====");
skipGps = true;
@ -240,6 +249,31 @@ class DestinationController extends GetxController {
// comment out by Akira, 2024-4-5
// skipGps = false;
// return;
// GPS信号が弱い場合、最後に取得した高いまたは中程度の位置情報を使用
if (okToUseGPS) {
double lastValidDistance = Geolocator.distanceBetween(
lastValidLat, lastValidLon,
d.lat!, d.lon!
);
/*
double lastValidDistance = distance.as(
LengthUnit.Meter,
LatLng(lastValidLat, lastValidLon),
LatLng(d.lat!, d.lon!),
);
*/
if (checkinRadious >= lastValidDistance || checkinRadious == -1) {
indexController.currentDestinationFeature.clear();
indexController.currentDestinationFeature.add(d);
} else {
skipGps = false;
return;
}
} else {
skipGps = false;
return;
}
}
if (isPhotoShoot.value == true) {
@ -407,10 +441,10 @@ class DestinationController extends GetxController {
context: Get.context!,
isScrollControlled: true,
builder: ((context) => CameraPage(
buyPointPhoto: true,
destination: d,
dbDest: ds.first,
))).whenComplete(() {
destination: d,
buyPointPhoto: true,
dbDest: ds.first,
))).whenComplete(() {
shouldShowBottomSheet = true;
skipGps = false;
rogainingCounted.value = true;
@ -641,13 +675,14 @@ class DestinationController extends GetxController {
});
} else {
Get.snackbar(
"始まっていません",
"ロゲ開始ボタンをタップして、ロゲイニングを始める必要があります",
icon: const Icon(Icons.assistant_photo_outlined, size: 40.0, color: Colors.blue),
snackPosition: SnackPosition.TOP,
duration: const Duration(seconds: 3),
backgroundColor: Colors.yellow,
);
"ロゲが始まっていません",
"ロゲ開始ボタンをタップして、ロゲイニングを始める必要があります",
icon: const Icon(
Icons.assistant_photo_outlined, size: 40.0, color: Colors.blue),
snackPosition: SnackPosition.TOP,
duration: const Duration(seconds: 3),
backgroundColor: Colors.yellow,
);
}
}
}
@ -680,6 +715,9 @@ class DestinationController extends GetxController {
// 目的地のリストを走査し、現在位置がチェックイン半径内にある場合は、チェックインの処理を行います。
// GPSデータの送信を開始します。
//
// 2024-4-8 Akira : See 2809
// checkForCheckinメソッドの再帰呼び出しをunawaitedで囲んで、非同期処理の結果を待たずに先に進むようにしました。また、再帰呼び出しの前に一定時間待機するようにしました。
//
Future<void> checkForCheckin() async {
//print("--- Start of checkForCheckin function ---");
dbService.updateDatabase();
@ -705,15 +743,16 @@ class DestinationController extends GetxController {
});
if (gps_push_started == false) {
pushGPStoServer();
unawaited( pushGPStoServer() );
}
//print("--- 123 ---- $skip_gps----");
} catch (e) {
//print("An error occurred: $e");
await checkForCheckin();
print("An error occurred: $e");
// await checkForCheckin();
} finally {
await Future.delayed(const Duration(seconds: 5)); // 一定時間待機してから再帰呼び出し
//print("--- End of checkForCheckin function, calling recursively ---");
await checkForCheckin();
unawaited( checkForCheckin() );
}
}
@ -772,6 +811,8 @@ class DestinationController extends GetxController {
// 買い物ポイントを作成する関数です。 指定された目的地に対して買い物ポイントの処理を行います。
//
// 買い物ポイントの作成に失敗した場合のエラーハンドリングを追加することを検討してください。
//
Future<void> makeBuyPoint(Destination destination, String imageurl) async {
DatabaseHelper db = DatabaseHelper.instance;
await db.updateBuyPoint(destination, imageurl);
@ -804,6 +845,8 @@ class DestinationController extends GetxController {
// チェックインを行う関数です。 指定された目的地に対してチェックインの処理を行います。
//
// 要検討:チェックインのリクエストが失敗した場合のエラーハンドリングを追加することをお勧めします。
//
Future<void> makeCheckin(
Destination destination, bool action, String imageurl) async {
// print("~~~~ calling checkin function ~~~~");
@ -873,6 +916,9 @@ class DestinationController extends GetxController {
@override
void onInit() async {
super.onInit();
// 要検討:エラーメッセージを表示するなどの適切な処理を追加することを検討してください。
//
locationController.locationMarkerPositionStream.listen(
(locationMarkerPosition) {
if (locationMarkerPosition != null) {
@ -896,9 +942,49 @@ class DestinationController extends GetxController {
// 現在位置とスタート地点との距離を計算します。
// 現在位置と前回の位置情報との距離と時間差を確認し、一定の条件を満たす場合はGPSデータをデータベースに追加します。
//
// 要検討GPSデータの追加に失敗した場合のエラーハンドリングを追加することをお勧めします。
//
void handleLocationUpdate(LocationMarkerPosition? position) async {
try {
if (position != null) {
final DestinationController destinationController = Get.find<DestinationController>();
final signalStrength = destinationController.getGpsSignalStrength();
okToUseGPS = false;
double prevLat = lastValidLat; // 一つ前の位置情報を記録
double prevLon = lastValidLon;
if (position!=null && (signalStrength == 'high' || signalStrength == 'medium')) {
// 信号強度が高いまたは中程度の場合、現在の位置情報を更新
currentLat = position.latitude;
currentLon = position.longitude;
lastValidLat = position.latitude;
lastValidLon = position.longitude;
okToUseGPS = true;
} else {
// 信号強度が低い場合、最後に取得した高いまたは中程度の位置情報を使用
// 但し、最初から高精度のものがない場合、どうするか?
//
if (lastValidLat != 0.0 && lastValidLon != 0.0) {
currentLat = lastValidLat;
currentLon = lastValidLon;
okToUseGPS = true;
} else {
// GPSの届く場所に行って、信号を拾ってください。とメッセージを出す。
position = null;
print("GPSの届く場所に行って、信号を拾ってください。");
Get.snackbar(
"GPS信号を正確に拾えていません",
"空が大きく見えるところへ行ってGPS信号を拾ってください。",
icon: const Icon(
Icons.assistant_photo_outlined, size: 40.0, color: Colors.blue),
snackPosition: SnackPosition.TOP,
duration: const Duration(seconds: 3),
backgroundColor: Colors.yellow,
);
}
}
if (okToUseGPS && position!=null) {
// スタート位置から150m離れたら、ready_for_goal
if (distanceToStart() >= 150) {
ready_for_goal = true;
}
@ -907,10 +993,11 @@ class DestinationController extends GetxController {
double distanceToDest = distance.as(
LengthUnit.Meter,
LatLng(position.latitude, position.longitude),
LatLng(currentLat, currentLon));
LatLng(prevLat, prevLon)
);
Duration difference =
lastGPSCollectedTime.difference(DateTime.now()).abs();
Duration difference = lastGPSCollectedTime.difference(DateTime.now()).abs();
// 最後にGPS信号を取得した時刻から10秒以上経過、かつ10m以上経過普通に歩くスピード
if (difference.inSeconds >= 10 || distanceToDest >= 10) {
// print(
// "^^^^^^^^ GPS data collected ${DateFormat('kk:mm:ss \n EEE d MMM').format(DateTime.now())}, ^^^ ${position.latitude}, ${position.longitude}");
@ -931,10 +1018,10 @@ class DestinationController extends GetxController {
currentLon = position.longitude;
}
*/
if (position != null) {
if (okToUseGPS) {
// 位置情報が取得できた場合、精度に関わらず最後の位置情報を更新
currentLat = position.latitude;
currentLon = position.longitude;
//currentLat = position.latitude;
//currentLon = position.longitude;
}
}
}
@ -1020,25 +1107,32 @@ class DestinationController extends GetxController {
if (token != null && token.isNotEmpty) {
await indexController.loadUserDetailsForToken(token);
fixMapBound(token);
return;
}else {
Get.toNamed(AppPages.LOGIN)!.then((value) {
if (indexController.currentUser.isNotEmpty) {
final tk = indexController.currentUser[0]["token"];
fixMapBound(tk);
} else {
Get.toNamed(AppPages.TRAVEL);
PerfectureService.getSubExt("9").then((value) {
if (value != null) {
LatLngBounds bnds = LatLngBounds(
LatLng(value[1], value[0]), LatLng(value[3], value[2]));
indexController.mapController
.fitBounds(bnds); //.centerZoomFitBounds(bnds);
}
});
}
});
}
Get.toNamed(AppPages.LOGIN)!.then((value) {
if (indexController.currentUser.isNotEmpty) {
final tk = indexController.currentUser[0]["token"];
fixMapBound(tk);
} else {
Get.toNamed(AppPages.TRAVEL);
PerfectureService.getSubExt("9").then((value) {
if (value != null) {
LatLngBounds bnds = LatLngBounds(
LatLng(value[1], value[0]), LatLng(value[3], value[2]));
indexController.mapController
.fitBounds(bnds); //.centerZoomFitBounds(bnds);
}
});
// 地図のイベントリスナーを設定
indexController.mapController.mapEventStream.listen((MapEvent mapEvent) {
if (mapEvent is MapEventMoveEnd) {
indexController.loadLocationsBound();
}
});
super.onReady();
}
@ -1106,7 +1200,7 @@ class DestinationController extends GetxController {
print("center is ${currentLon}, ${currentLon}");
return true;
}());
// Akira
// Akira ... 状況によって呼ぶか呼ばないか
if (currentLat != 0 || currentLon != 0) {
indexController.mapController.move(LatLng(currentLat, currentLon), 17.0);
}
@ -1236,22 +1330,36 @@ class DestinationController extends GetxController {
// 目的地の選択状態を切り替える関数です。
//
void toggleSelection(Destination dest) async {
DatabaseHelper db = DatabaseHelper.instance;
await db.toggleSelecttion(dest);
destinations.clear();
db.getDestinations().then((value) {
destinationCount.value = 0;
currentSelectedDestinations.clear();
for (Destination d in value) {
//print("------ destination controller populating destination-------- ${d.checkedin}-------- :::::");
//print("-----populated----- ${d.toMap()}");
if (d.selected!) {
currentSelectedDestinations.add(d);
try {
DatabaseHelper db = DatabaseHelper.instance;
await db.toggleSelecttion(dest);
destinations.clear();
db.getDestinations().then((value) {
destinationCount.value = 0;
currentSelectedDestinations.clear();
for (Destination d in value) {
//print("------ destination controller populating destination-------- ${d.checkedin}-------- :::::");
//print("-----populated----- ${d.toMap()}");
if (d.selected!) {
currentSelectedDestinations.add(d);
}
destinations.add(d);
}
destinations.add(d);
}
destinationCount.value = destinations.length;
});
destinationCount.value = destinations.length;
});
} catch( e ){
print('Error in toggleSelection: $e');
Get.snackbar(
"画面切り替えでエラー",
"画面の切り替えができませんでした",
icon: const Icon(
Icons.assistant_photo_outlined, size: 40.0, color: Colors.blue),
snackPosition: SnackPosition.TOP,
duration: const Duration(seconds: 3),
backgroundColor: Colors.yellow,
);
}
}
// ダイアログを表示する関数です。

View File

@ -1,252 +0,0 @@
// import 'package:flutter/material.dart';
// import 'package:geolocator/geolocator.dart';
// import 'package:get/get.dart';
// import 'package:latlong2/latlong.dart';
// import 'package:rogapp/pages/destination/destination_controller.dart';
// import 'package:rogapp/pages/destination_map/destination_map_page.dart';
// import 'package:rogapp/pages/drawer/drawer_page.dart';
// import 'package:rogapp/pages/index/index_controller.dart';
// import 'package:rogapp/routes/app_pages.dart';
// import 'package:rogapp/widgets/destination_widget.dart';
// class XDestnationPage extends StatelessWidget {
// XDestnationPage({Key? key}) : super(key: key);
// final DestinationController destinationController =
// Get.find<DestinationController>();
// final IndexController indexController = Get.find<IndexController>();
// final List<int> _items = List<int>.generate(50, (int index) => index);
// Future<void> showCurrentPosition() async {
// LocationPermission permission = await Geolocator.checkPermission();
// if (permission != LocationPermission.whileInUse ||
// permission != LocationPermission.always) {
// permission = await Geolocator.requestPermission();
// }
// Position position = await Geolocator.getCurrentPosition(
// desiredAccuracy: LocationAccuracy.high,
// forceAndroidLocationManager: true);
// indexController.rogMapController
// .move(LatLng(position.latitude, position.longitude), 14);
// }
// Image getImage(int index) {
// if (destinationController.destinations[index].photos == null ||
// destinationController.destinations[index].photos == "") {
// return const Image(image: AssetImage('assets/images/empty_image.png'));
// } else {
// return Image(
// image:
// NetworkImage(destinationController.destinations[index].photos!));
// }
// }
// Widget getRoutingImage(int route) {
// switch (route) {
// case 0:
// return const Image(
// image: AssetImage('assets/images/p4_9_man.png'),
// width: 35.0,
// );
// case 1:
// return const Image(
// image: AssetImage('assets/images/p4_8_car.png'),
// width: 35.0,
// );
// case 2:
// return const Image(
// image: AssetImage('assets/images/p4_10_train.png'),
// width: 35.0,
// );
// default:
// return const Image(
// image: AssetImage('assets/images/p4_9_man.png'),
// width: 35.0,
// );
// }
// }
// @override
// Widget build(BuildContext context) {
// return WillPopScope(
// onWillPop: () async {
// indexController.switchPage(AppPages.INITIAL);
// return false;
// },
// child: Scaffold(
// drawer: DrawerPage(),
// bottomNavigationBar: BottomAppBar(
// child: Row(
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
// children: <Widget>[
// Padding(
// padding: const EdgeInsets.only(left: 13.0),
// child: InkWell(
// child: Obx((() => getRoutingImage(
// destinationController.travelMode.value))),
// onTap: () {
// Get.bottomSheet(
// Obx(
// () => ListView(
// children: [
// Padding(
// padding: const EdgeInsets.only(
// top: 30.0, bottom: 30),
// child: Center(
// child: Text(
// "select_travel_mode".tr,
// style: const TextStyle(
// fontSize: 22.0,
// color: Colors.red,
// fontWeight: FontWeight.bold),
// ),
// ),
// ),
// ListTile(
// selected:
// destinationController.travelMode.value ==
// 0
// ? true
// : false,
// selectedTileColor: Colors.amber.shade200,
// leading: const Image(
// image: AssetImage(
// 'assets/images/p4_9_man.png'),
// ),
// title: Text("walking".tr),
// onTap: () {
// destinationController.travelMode.value = 0;
// destinationController
// .populateDestinations();
// Get.back();
// },
// ),
// ListTile(
// selected:
// destinationController.travelMode.value ==
// 1
// ? true
// : false,
// selectedTileColor: Colors.amber.shade200,
// leading: const Image(
// image: AssetImage(
// 'assets/images/p4_8_car.png'),
// ),
// title: Text("driving".tr),
// onTap: () {
// destinationController.travelMode.value = 1;
// destinationController
// .populateDestinations();
// Get.back();
// },
// ),
// // ListTile(
// // selected: destinationController.travelMode == 2 ? true : false,
// // selectedTileColor: Colors.amber.shade200,
// // leading: Image(image: AssetImage('assets/images/p4_10_train.png'),),
// // title: Text("transit".tr),
// // onTap:(){
// // destinationController.travelMode.value = 2;
// // destinationController.PopulateDestinations();
// // Get.back();
// // },
// // ),
// ],
// ),
// ),
// isScrollControlled: false,
// backgroundColor: Colors.white,
// );
// //destinationController.PopulateDestinations();
// }),
// ),
// IconButton(
// icon: const Icon(
// Icons.travel_explore,
// size: 35,
// ),
// onPressed: () {
// indexController.switchPage(AppPages.INITIAL);
// }),
// ],
// ),
// ),
// floatingActionButton: FloatingActionButton(
// onPressed: () {
// //print("######");
// indexController.toggleDestinationMode();
// },
// tooltip: 'Increment',
// elevation: 4.0,
// child: Obx(() => indexController.desinationMode.value == 1
// ? const Image(image: AssetImage('assets/images/list2.png'))
// : const Image(image: AssetImage('assets/images/map.png'))),
// ),
// floatingActionButtonLocation:
// FloatingActionButtonLocation.centerDocked,
// appBar: AppBar(
// automaticallyImplyLeading: true,
// title: Text("app_title".tr),
// actions: [
// InkWell(
// onTap: () {
// Get.toNamed(AppPages.CAMERA_PAGE);
// },
// child: destinationController.isInRog.value == true
// ? Image.asset(
// "assets/images/basic-walking.gif",
// height: 10.0,
// )
// : destinationController.isAtGoal.value == true
// ? IconButton(
// onPressed: () {
// Get.toNamed(AppPages.CAMERA_PAGE);
// },
// icon: const Icon(Icons.assistant_photo),
// )
// : IconButton(
// onPressed: () {
// Get.toNamed(AppPages.CAMERA_PAGE);
// },
// icon: const Icon(Icons.accessibility),
// ),
// ),
// // Obx(() =>
// // Text(indexController.connectionStatusName.value)
// // ),
// Obx(
// () => ToggleButtons(
// disabledColor: Colors.grey.shade200,
// selectedColor: Colors.red,
// onPressed: (int index) {
// destinationController.isGpsSelected.value =
// !destinationController.isGpsSelected.value;
// if (destinationController.isGpsSelected.value) {
// destinationController.chekcs = 0;
// destinationController.skipGps = false;
// //destinationController.resetRogaining();
// }
// },
// isSelected: [destinationController.isGpsSelected.value],
// children: const <Widget>[
// Icon(
// Icons.explore,
// size: 35.0,
// )
// ],
// ),
// ),
// // IconButton(onPressed: (){
// // showCurrentPosition();
// // },
// // icon: Icon(Icons.location_on_outlined))
// ],
// ),
// body: Obx(() => indexController.desinationMode.value == 0
// ? DestinationWidget()
// : DestinationMapPage())),
// );
// }
// }

View File

@ -34,6 +34,8 @@ class DestinationMapPage extends StatelessWidget {
return pts;
}
// 要検討:マーカーのタップイベントを処理する際に、エラーハンドリングが不十分です。例外が発生した場合の処理を追加することをお勧めします。
//
List<Marker>? getMarkers() {
List<Marker> pts = [];
int index = -1;
@ -123,6 +125,8 @@ class DestinationMapPage extends StatelessWidget {
)));
}
// 要検討MapOptionsのboundsプロパティにハードコードされた座標が使用されています。これを動的に設定できるようにすることを検討してください。
//
FlutterMap travelMap() {
return FlutterMap(
mapController: indexController.rogMapController,

View File

@ -11,6 +11,8 @@ class DrawerPage extends StatelessWidget {
final IndexController indexController = Get.find<IndexController>();
// 要検討URLの起動に失敗した場合のエラーハンドリングが不十分です。適切なエラーメッセージを表示するなどの処理を追加してください。
//
void _launchURL(url) async {
if (!await launchUrl(url)) throw 'Could not launch $url';
}
@ -90,6 +92,8 @@ class DrawerPage extends StatelessWidget {
leading: const Icon(Icons.password),
title: const Text("リセット"),
onTap: () {
// 要検討:リセット操作の確認メッセージをローカライズすることを検討してください。
//
Get.defaultDialog(
title: "よろしいですか、リセットしますか?",
middleText: "これにより、すべてのゲーム データが削除され、すべての状態が削除されます",
@ -132,6 +136,8 @@ class DrawerPage extends StatelessWidget {
),
indexController.currentUser.isNotEmpty
? ListTile(
// 要検討:アカウント削除のリクエストが失敗した場合のエラーハンドリングを追加することをお勧めします。
//
leading: const Icon(Icons.delete_forever),
title: Text("ユーザーデータを削除する".tr),
onTap: () {

View File

@ -31,6 +31,8 @@ class _GpsPageState extends State<GpsPage> {
loadGpsData();
}
// 要検討GPSデータの読み込みに失敗した場合のエラーハンドリングが不十分です。適切なエラーメッセージを表示するなどの処理を追加してください。
//
void loadGpsData() async {
final team_name = indexController.currentUser[0]["user"]['team_name'];
final event_code = indexController.currentUser[0]["user"]["event_code"];
@ -41,6 +43,8 @@ class _GpsPageState extends State<GpsPage> {
//print("--- gps data ${data} ----");
}
// 要検討:マーカーの形状を決定する際に、マジックナンバーが使用されています。定数を使用するなどして、コードの可読性を向上させることを検討してください。
//
Widget getMarkerShape(GpsData i) {
return Row(
mainAxisAlignment: MainAxisAlignment.start,

View File

@ -24,6 +24,8 @@ class _HistoryPageState extends State<HistoryPage> {
child: Column(
children: [
FutureBuilder(
// 要検討:スナップショットのエラーハンドリングが行われていますが、具体的なエラーメッセージを表示するようにすることをお勧めします。
//
future: db.getDestinations(),
builder: (BuildContext context,
AsyncSnapshot<List<Destination>> snapshot) {
@ -49,6 +51,7 @@ class _HistoryPageState extends State<HistoryPage> {
return Padding(
padding: const EdgeInsets.all(8.0),
child: CustomWidget(
// 要検討:画像のサイズがハードコードされています。画像のサイズを動的に設定できるようにすることを検討してください。
title: dests[index].name!,
subtitle:
"${dests[index].sub_loc_id} : ${dests[index].name}",

View File

@ -2,6 +2,8 @@ import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:rogapp/pages/search/search_page.dart';
// 要検討SearchPageへのナビゲーションにNavigator.pushを使用していますが、一貫性のためにGet.toやGet.toNamedを使用することを検討してください。
//
class HomePage extends GetView{
const HomePage({Key? key}) : super(key: key);

View File

@ -166,6 +166,8 @@ class IndexController extends GetxController {
return LatLngBounds(LatLng(x1!, y1!), LatLng(x0!, y0!));
}
// 要検討:エラーハンドリングが行われていますが、エラーメッセージをローカライズすることを検討してください。
//
void login(String email, String password, BuildContext context) {
AuthService.login(email, password).then((value) {
print("------- logged in user details ######## $value ###### --------");
@ -189,6 +191,8 @@ class IndexController extends GetxController {
});
}
// 要検討:エラーハンドリングが行われていますが、エラーメッセージをローカライズすることを検討してください。
//
void changePassword(
String oldpassword, String newpassword, BuildContext context) {
String token = currentUser[0]['token'];
@ -230,6 +234,8 @@ class IndexController extends GetxController {
cats.clear();
}
// 要検討:エラーハンドリングが行われていますが、エラーメッセージをローカライズすることを検討してください。
//
void register(String email, String password, BuildContext context) {
AuthService.register(email, password).then((value) {
if (value.isNotEmpty) {
@ -345,14 +351,20 @@ class IndexController extends GetxController {
// 2024-04-03 Akira .. Update the code . See ticket 2800.
//
// 2024-4-8 Akira : See 2809
// IndexControllerクラスでは、Future.delayedの呼び出しをunawaitedで囲んで、非同期処理の結果を待たずに先に進むようにしました。これにより、メモリリークを防ぐことができます
//
// 要検討Future.delayedを使用して非同期処理を待たずに先に進むようにしていますが、これによってメモリリークが発生する可能性があります。非同期処理の結果を適切に処理することを検討してください。
//
void loadLocationsBound() async {
if (isCustomAreaSelected.value == true) {
return;
}
// Akira 追加:2024-4-6 #2800
await waitForMapControllerReady(); // MapControllerの初期化が完了するまで待機
//await waitForMapControllerReady(); // MapControllerの初期化が完了するまで待機
// Akira 追加:2024-4-6 #2800
// ==> remove 2024-4-8
locations.clear();
String cat = currentCat.isNotEmpty ? currentCat[0] : "";
@ -375,22 +387,25 @@ class IndexController extends GetxController {
isLoading.value = true; // ローディング状態をtrueに設定
Future.delayed(const Duration(seconds: 1), () async {
// unawaited( Future.delayed(const Duration(seconds: 1), () async {
// remove
//print("bounds --- (${bounds.southWest.latitude},${bounds.southWest.longitude}),(${bounds.northWest.latitude},${bounds.northWest.longitude}),(${bounds.northEast.latitude},${bounds.northEast.longitude}),(${bounds.southEast.latitude},${bounds.southEast.longitude})");
var value = await LocationService.loadLocationsBound(
bounds.southWest.latitude,
bounds.southWest.longitude,
bounds.northWest.latitude,
bounds.northWest.longitude,
bounds.northEast.latitude,
bounds.northEast.longitude,
bounds.southEast.latitude,
bounds.southEast.longitude,
cat
);
if ( value == null ) {
// 要検討APIからのレスポンスがnullの場合のエラーハンドリングが不十分です。適切なエラーメッセージを表示するなどの処理を追加してください。
try {
final value = await LocationService.loadLocationsBound(
bounds.southWest.latitude,
bounds.southWest.longitude,
bounds.northWest.latitude,
bounds.northWest.longitude,
bounds.northEast.latitude,
bounds.northEast.longitude,
bounds.southEast.latitude,
bounds.southEast.longitude,
cat
);
if (value == null) {
// APIからのレスポンスがnullの場合
print("LocationService.loadLocationsBound からの回答がnullのため、マップをリロード");
//DestinationController destinationController = Get.find<DestinationController>(); // 追加
@ -399,32 +414,41 @@ class IndexController extends GetxController {
// destinationController.fixMapBound(tk); // 追加
//} // 追加
return;
}
isLoading.value = false; // ローディング状態をfalseに設定
if (value!=null && value.features.isEmpty) {
}
isLoading.value = false; // ローディング状態をfalseに設定
if (value != null && value.features.isEmpty) {
if (showPopup == false) {
return;
}
Get.snackbar(
"Too many Points",
"please zoom in",
icon: const Icon(Icons.assistant_photo_outlined, size: 40.0, color: Colors.blue),
icon: const Icon(
Icons.assistant_photo_outlined, size: 40.0, color: Colors.blue),
snackPosition: SnackPosition.TOP,
duration: const Duration(seconds: 3),
backgroundColor: Colors.yellow,
);
showPopup = false;
}
if (value!=null && value.features.isNotEmpty) {
if (value != null && value.features.isNotEmpty) {
locations.add(value);
}
});
} catch ( e) {
print("Error in loadLocationsBound: $e");
// エラーが発生した場合のリトライ処理や適切なエラーメッセージの表示を行う
// 例えば、一定時間後に再度loadLocationsBound()を呼び出すなど
}));
}
//===Akira 追加:2024-4-6 #2800
// 要検討MapControllerの初期化が完了するまで待機していますが、タイムアウトを設定することを検討してください。
// 初期化に時間がかかりすぎる場合、ユーザーにわかりやすいメッセージを表示するようにしてください。
//
Future<void> waitForMapControllerReady() async {
if (!isMapControllerReady.value) {
await Future.doWhile(() async {

View File

@ -22,6 +22,9 @@ import 'package:rogapp/utils/location_controller.dart';
// 全体的に、index_page.dartはアプリのメインページの構造を定義し、他のコンポーネントやページへの橋渡しを行っているファイルです。
//
// 要検討GPSデータの表示アイコンをタップした際のエラーハンドリングを追加することをお勧めします。
// MapWidgetとListWidgetの切り替えにObxを使用していますが、パフォーマンスを考慮して、必要な場合にのみウィジェットを再構築するようにしてください。
// DestinationControllerのisSimulationModeを使用してGPS信号の強弱をシミュレーションしていますが、本番環境では適切に実際のGPS信号を使用するようにしてください。
// IndexPageクラスは、GetView<IndexController>を継承したStatelessWidgetです。このクラスは、アプリのメインページを表すウィジェットです。
//

View File

@ -2,6 +2,9 @@ import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:rogapp/routes/app_pages.dart';
// 要検討:ログインボタンとサインアップボタンの配色を見直すことを検討してください。現在の配色では、ボタンの役割がわかりにくい可能性があります。
// ボタンのテキストをローカライズすることを検討してください。
//
class LandingPage extends StatefulWidget {
const LandingPage({ Key? key }) : super(key: key);

View File

@ -3,6 +3,8 @@ import 'package:flutter/material.dart';
class LoadingPage extends StatelessWidget {
const LoadingPage({Key? key}) : super(key: key);
// 要検討ローディングインジケーターの値を固定値0.8)にしていますが、実際のローディング進捗に合わせて動的に変更することを検討してください。
//
@override
Widget build(BuildContext context) {
return Container(

View File

@ -3,6 +3,10 @@ import 'package:get/get.dart';
import 'package:rogapp/pages/index/index_controller.dart';
import 'package:rogapp/routes/app_pages.dart';
// 要検討:ログインボタンとサインアップボタンの配色を見直すことを検討してください。現在の配色では、ボタンの役割がわかりにくい可能性があります。
// エラーメッセージをローカライズすることを検討してください。
// ログイン処理中にエラーが発生した場合のエラーハンドリングを追加することをお勧めします。
//
class LoginPage extends StatelessWidget {
final IndexController indexController = Get.find<IndexController>();

View File

@ -3,6 +3,10 @@ import 'package:get/get.dart';
import 'package:rogapp/pages/index/index_controller.dart';
import 'package:rogapp/routes/app_pages.dart';
// 要検討:ログインボタンとサインアップボタンの配色を見直すことを検討してください。現在の配色では、ボタンの役割がわかりにくい可能性があります。
// エラーメッセージをローカライズすることを検討してください。
// ポップアップを閉じるボタンを追加することを検討してください。
//
class LoginPopupPage extends StatelessWidget {
LoginPopupPage({Key? key}) : super(key: key);

View File

@ -70,6 +70,8 @@ class _PermissionHandlerScreenState extends State<PermissionHandlerScreen> {
));
}
// 要検討:位置情報の許可が拒否された場合、適切なエラーメッセージを表示することを検討してください。
//
Future<void> requestPermission() async {
PermissionStatus permission = await Permission.location.status;
if (permission == PermissionStatus.permanentlyDenied) {
@ -93,6 +95,8 @@ class _PermissionHandlerScreenState extends State<PermissionHandlerScreen> {
);
}
// 要検討:ユーザーが位置情報の許可を拒否し続けた場合の対処方法を明確にすることをお勧めします。
//
void showPermanentAlert() {
showDialog(
context: context,