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

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