Fixed Android issues
This commit is contained in:
3
TODO.txt
3
TODO.txt
@ -11,3 +11,6 @@
|
|||||||
起動時に最後の参加イベントが過去日だったら、
|
起動時に最後の参加イベントが過去日だったら、
|
||||||
チェックポイントをクリアする。
|
チェックポイントをクリアする。
|
||||||
当日なら、参加処理?をしてタイトルを変える。
|
当日なら、参加処理?をしてタイトルを変える。
|
||||||
|
|
||||||
|
チーム構成とエントリーの相関が難しいのでは??
|
||||||
|
|
||||||
|
|||||||
@ -88,6 +88,9 @@ Future<void> saveGameState() async {
|
|||||||
pref.setBool(
|
pref.setBool(
|
||||||
"rogaining_counted", destinationController.rogainingCounted.value);
|
"rogaining_counted", destinationController.rogainingCounted.value);
|
||||||
pref.setBool("ready_for_goal", DestinationController.ready_for_goal);
|
pref.setBool("ready_for_goal", DestinationController.ready_for_goal);
|
||||||
|
|
||||||
|
// 最後のゲーム日時を保存
|
||||||
|
pref.setString('lastGameDate', DateTime.now().toIso8601String());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -108,6 +111,20 @@ Future<void> restoreGame() async {
|
|||||||
if (indexController.currentUser.isNotEmpty &&
|
if (indexController.currentUser.isNotEmpty &&
|
||||||
indexController.currentUser[0]["user"]["id"] == savedUserId) {
|
indexController.currentUser[0]["user"]["id"] == savedUserId) {
|
||||||
|
|
||||||
|
// 最後のゲーム日時を取得
|
||||||
|
final lastGameDateString = pref.getString('lastGameDate');
|
||||||
|
if (lastGameDateString != null) {
|
||||||
|
final lastGameDate = DateTime.parse(lastGameDateString);
|
||||||
|
final now = DateTime.now();
|
||||||
|
|
||||||
|
// 最後のゲームが昨日以前の場合
|
||||||
|
if (lastGameDate.isBefore(DateTime(now.year, now.month, now.day))) {
|
||||||
|
// ゲームの状態をクリア
|
||||||
|
await resetGameState();
|
||||||
|
return; // ここで関数を終了
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
final dateString = pref.getString('eventDate');
|
final dateString = pref.getString('eventDate');
|
||||||
if (dateString != null) {
|
if (dateString != null) {
|
||||||
final parsedDate = DateTime.parse(dateString);
|
final parsedDate = DateTime.parse(dateString);
|
||||||
@ -143,6 +160,24 @@ Future<void> restoreGame() async {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ゲームの状態をリセットする関数
|
||||||
|
Future<void> resetGameState() async {
|
||||||
|
SharedPreferences pref = await SharedPreferences.getInstance();
|
||||||
|
await pref.remove("is_in_rog");
|
||||||
|
await pref.remove("rogaining_counted");
|
||||||
|
await pref.remove("ready_for_goal");
|
||||||
|
|
||||||
|
DestinationController destinationController = Get.find<DestinationController>();
|
||||||
|
destinationController.isInRog.value = false;
|
||||||
|
destinationController.rogainingCounted.value = false;
|
||||||
|
DestinationController.ready_for_goal = false;
|
||||||
|
|
||||||
|
// チェックポイントをクリア
|
||||||
|
destinationController.deleteDBDestinations();
|
||||||
|
|
||||||
|
debugPrint("Game state has been reset due to outdated last game date");
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
void restoreGame_new() async {
|
void restoreGame_new() async {
|
||||||
SharedPreferences pref = await SharedPreferences.getInstance();
|
SharedPreferences pref = await SharedPreferences.getInstance();
|
||||||
|
|||||||
@ -32,6 +32,8 @@ class EntryController extends GetxController {
|
|||||||
final activeEvents = <Event>[].obs; //有効なイベントリスト
|
final activeEvents = <Event>[].obs; //有効なイベントリスト
|
||||||
|
|
||||||
final teamMembers = <User>[].obs;
|
final teamMembers = <User>[].obs;
|
||||||
|
final hasError = false.obs;
|
||||||
|
final errorMessage = "".obs;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onInit() async {
|
void onInit() async {
|
||||||
@ -45,7 +47,7 @@ class EntryController extends GetxController {
|
|||||||
_apiService = await Get.putAsync(() => ApiService().init());
|
_apiService = await Get.putAsync(() => ApiService().init());
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print('Error initializing ApiService: $e');
|
print('Error initializing ApiService: $e');
|
||||||
Get.snackbar('Error', 'Failed to initialize API service');
|
Get.snackbar('Error', 'APIサービスの初期化に失敗しました');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -64,14 +66,16 @@ class EntryController extends GetxController {
|
|||||||
initializeEditMode(currentEntry.value!);
|
initializeEditMode(currentEntry.value!);
|
||||||
} else {
|
} else {
|
||||||
// 新規作成モードの場合、最初のイベントを選択
|
// 新規作成モードの場合、最初のイベントを選択
|
||||||
if (events.isNotEmpty) {
|
if (activeEvents.isNotEmpty) {
|
||||||
selectedEvent.value = activeEvents.first;
|
selectedEvent.value = activeEvents.first;
|
||||||
selectedDate.value = activeEvents.first.startDatetime;
|
selectedDate.value = activeEvents.first.startDatetime;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
print('Error initializing data: $e');
|
print('Error initializing data: $e');
|
||||||
Get.snackbar('Error', 'Failed to load initial data');
|
// エラー状態を設定
|
||||||
|
hasError.value = true;
|
||||||
|
Get.snackbar('Error', '初期データの読み込みに失敗しました');
|
||||||
} finally {
|
} finally {
|
||||||
isLoading.value = false;
|
isLoading.value = false;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import 'package:gifunavi/model/event.dart';
|
|||||||
import 'package:gifunavi/model/category.dart';
|
import 'package:gifunavi/model/category.dart';
|
||||||
import 'package:gifunavi/model/team.dart';
|
import 'package:gifunavi/model/team.dart';
|
||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
|
import 'package:gifunavi/widgets/error_widget.dart';
|
||||||
|
|
||||||
import 'package:timezone/timezone.dart' as tz;
|
import 'package:timezone/timezone.dart' as tz;
|
||||||
|
|
||||||
@ -31,6 +32,17 @@ class EntryDetailPage extends GetView<EntryController> {
|
|||||||
if (controller.isLoading.value) {
|
if (controller.isLoading.value) {
|
||||||
return const Center(child: CircularProgressIndicator());
|
return const Center(child: CircularProgressIndicator());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (controller.hasError.value) {
|
||||||
|
return CustomErrorWidget(
|
||||||
|
errorMessage: controller.errorMessage.value,
|
||||||
|
onRetry: () => controller.loadInitialData(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
try {
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.all(16.0),
|
padding: const EdgeInsets.all(16.0),
|
||||||
child: SingleChildScrollView(
|
child: SingleChildScrollView(
|
||||||
@ -41,9 +53,22 @@ class EntryDetailPage extends GetView<EntryController> {
|
|||||||
label: 'イベント',
|
label: 'イベント',
|
||||||
items: controller.activeEvents,
|
items: controller.activeEvents,
|
||||||
selectedId: controller.selectedEvent.value?.id,
|
selectedId: controller.selectedEvent.value?.id,
|
||||||
|
|
||||||
|
onChanged: (eventId) {
|
||||||
|
final event = controller.activeEvents.firstWhereOrNull((
|
||||||
|
e) => e.id == eventId);
|
||||||
|
if (event != null) {
|
||||||
|
controller.updateEvent(event);
|
||||||
|
} else {
|
||||||
|
print('Event with id $eventId not found');
|
||||||
|
// 必要に応じてエラー処理を追加
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/*
|
||||||
onChanged: (eventId) => controller.updateEvent(
|
onChanged: (eventId) => controller.updateEvent(
|
||||||
controller.activeEvents.firstWhere((e) => e.id == eventId)
|
controller.activeEvents.firstWhere((e) => e.id == eventId)
|
||||||
),
|
),
|
||||||
|
*/
|
||||||
getDisplayName: (event) => event.eventName,
|
getDisplayName: (event) => event.eventName,
|
||||||
getId: (event) => event.id,
|
getId: (event) => event.id,
|
||||||
),
|
),
|
||||||
@ -52,9 +77,21 @@ class EntryDetailPage extends GetView<EntryController> {
|
|||||||
label: 'チーム',
|
label: 'チーム',
|
||||||
items: controller.teams,
|
items: controller.teams,
|
||||||
selectedId: controller.selectedTeam.value?.id,
|
selectedId: controller.selectedTeam.value?.id,
|
||||||
|
onChanged: (teamId) {
|
||||||
|
final team = controller.teams.firstWhereOrNull((t) =>
|
||||||
|
t.id == teamId);
|
||||||
|
if (team != null) {
|
||||||
|
controller.updateTeam(team);
|
||||||
|
} else {
|
||||||
|
print('Team with id $teamId not found');
|
||||||
|
// 必要に応じてエラー処理を追加
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/*
|
||||||
onChanged: (teamId) => controller.updateTeam(
|
onChanged: (teamId) => controller.updateTeam(
|
||||||
controller.teams.firstWhere((t) => t.id == teamId)
|
controller.teams.firstWhere((t) => t.id == teamId)
|
||||||
),
|
),
|
||||||
|
*/
|
||||||
getDisplayName: (team) => team.teamName,
|
getDisplayName: (team) => team.teamName,
|
||||||
getId: (team) => team.id,
|
getId: (team) => team.id,
|
||||||
),
|
),
|
||||||
@ -78,7 +115,9 @@ class EntryDetailPage extends GetView<EntryController> {
|
|||||||
title: const Text('日付'),
|
title: const Text('日付'),
|
||||||
subtitle: Text(
|
subtitle: Text(
|
||||||
controller.selectedDate.value != null
|
controller.selectedDate.value != null
|
||||||
? DateFormat('yyyy-MM-dd').format(tz.TZDateTime.from(controller.selectedDate.value!, tz.getLocation('Asia/Tokyo')))
|
? DateFormat('yyyy-MM-dd').format(tz.TZDateTime.from(
|
||||||
|
controller.selectedDate.value!,
|
||||||
|
tz.getLocation('Asia/Tokyo')))
|
||||||
: '日付を選択してください',
|
: '日付を選択してください',
|
||||||
),
|
),
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
@ -86,25 +125,37 @@ class EntryDetailPage extends GetView<EntryController> {
|
|||||||
Get.snackbar('Error', 'Please select an event first');
|
Get.snackbar('Error', 'Please select an event first');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final tz.TZDateTime now = tz.TZDateTime.now(tz.getLocation('Asia/Tokyo'));
|
final tz.TZDateTime now = tz.TZDateTime.now(tz
|
||||||
final tz.TZDateTime eventStart = tz.TZDateTime.from(controller.selectedEvent.value!.startDatetime, tz.getLocation('Asia/Tokyo'));
|
.getLocation('Asia/Tokyo'));
|
||||||
final tz.TZDateTime eventEnd = tz.TZDateTime.from(controller.selectedEvent.value!.endDatetime, tz.getLocation('Asia/Tokyo'));
|
final tz.TZDateTime eventStart = tz.TZDateTime.from(
|
||||||
|
controller.selectedEvent.value!.startDatetime, tz
|
||||||
|
.getLocation('Asia/Tokyo'));
|
||||||
|
final tz.TZDateTime eventEnd = tz.TZDateTime.from(
|
||||||
|
controller.selectedEvent.value!.endDatetime, tz
|
||||||
|
.getLocation('Asia/Tokyo'));
|
||||||
|
|
||||||
final tz.TZDateTime initialDate = controller.selectedDate.value != null
|
final tz.TZDateTime initialDate = controller.selectedDate
|
||||||
? tz.TZDateTime.from(controller.selectedDate.value!, tz.getLocation('Asia/Tokyo'))
|
.value != null
|
||||||
|
? tz.TZDateTime.from(controller.selectedDate.value!,
|
||||||
|
tz.getLocation('Asia/Tokyo'))
|
||||||
: (now.isAfter(eventStart) ? now : eventStart);
|
: (now.isAfter(eventStart) ? now : eventStart);
|
||||||
|
|
||||||
// 選択可能な最初の日付を設定(今日かイベント開始日のうち、より後の日付)
|
// 選択可能な最初の日付を設定(今日かイベント開始日のうち、より後の日付)
|
||||||
final tz.TZDateTime firstDate = now.isAfter(eventStart) ? now : eventStart;
|
final tz.TZDateTime firstDate = now.isAfter(eventStart)
|
||||||
|
? now
|
||||||
|
: eventStart;
|
||||||
|
|
||||||
final DateTime? picked = await showDatePicker(
|
final DateTime? picked = await showDatePicker(
|
||||||
context: context,
|
context: context,
|
||||||
initialDate: initialDate.isAfter(firstDate) ? initialDate : firstDate,
|
initialDate: initialDate.isAfter(firstDate)
|
||||||
|
? initialDate
|
||||||
|
: firstDate,
|
||||||
firstDate: firstDate,
|
firstDate: firstDate,
|
||||||
lastDate: eventEnd,
|
lastDate: eventEnd,
|
||||||
);
|
);
|
||||||
if (picked != null) {
|
if (picked != null) {
|
||||||
controller.updateDate(tz.TZDateTime.from(picked, tz.getLocation('Asia/Tokyo')));
|
controller.updateDate(tz.TZDateTime.from(
|
||||||
|
picked, tz.getLocation('Asia/Tokyo')));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@ -151,6 +202,12 @@ class EntryDetailPage extends GetView<EntryController> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
} catch (e) {
|
||||||
|
print('Error in EntryDetailPage: $e');
|
||||||
|
return const Center(
|
||||||
|
child: Text('エラーが発生しました。もう一度お試しください。'),
|
||||||
|
);
|
||||||
|
}
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -166,11 +223,17 @@ class EntryDetailPage extends GetView<EntryController> {
|
|||||||
return DropdownButtonFormField<int>(
|
return DropdownButtonFormField<int>(
|
||||||
decoration: InputDecoration(labelText: label),
|
decoration: InputDecoration(labelText: label),
|
||||||
value: selectedId,
|
value: selectedId,
|
||||||
items: items.map((item) => DropdownMenuItem<int>(
|
items: items.isNotEmpty ? items.map((item) => DropdownMenuItem<int>(
|
||||||
|
//items: items.map((item) => DropdownMenuItem<int>(
|
||||||
value: getId(item),
|
value: getId(item),
|
||||||
child: Text(getDisplayName(item)),
|
child: Text(getDisplayName(item)),
|
||||||
)).toList(),
|
)).toList() : null,
|
||||||
onChanged: onChanged,
|
onChanged: (value) {
|
||||||
|
if (value != null) {
|
||||||
|
onChanged(value);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
//onChanged: onChanged,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -180,11 +243,18 @@ class EntryDetailPage extends GetView<EntryController> {
|
|||||||
return DropdownButtonFormField<NewCategory>(
|
return DropdownButtonFormField<NewCategory>(
|
||||||
decoration: InputDecoration(labelText: 'カテゴリ'),
|
decoration: InputDecoration(labelText: 'カテゴリ'),
|
||||||
value: controller.selectedCategory.value,
|
value: controller.selectedCategory.value,
|
||||||
items: eligibleCategories.map((category) => DropdownMenuItem<NewCategory>(
|
items: eligibleCategories.isNotEmpty ? eligibleCategories.map((category) => DropdownMenuItem<NewCategory>(
|
||||||
|
|
||||||
|
//items: eligibleCategories.map((category) => DropdownMenuItem<NewCategory>(
|
||||||
value: category,
|
value: category,
|
||||||
child: Text(category.categoryName),
|
child: Text(category.categoryName),
|
||||||
)).toList(),
|
)).toList() : null,
|
||||||
onChanged: (value) => controller.updateCategory(value),
|
onChanged: (value) {
|
||||||
|
if (value != null) {
|
||||||
|
controller.updateCategory(value);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
//onChanged: (value) => controller.updateCategory(value),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
35
lib/widgets/error_widget.dart
Normal file
35
lib/widgets/error_widget.dart
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
|
||||||
|
class CustomErrorWidget extends StatelessWidget {
|
||||||
|
final String errorMessage;
|
||||||
|
final VoidCallback onRetry;
|
||||||
|
|
||||||
|
const CustomErrorWidget({
|
||||||
|
Key? key,
|
||||||
|
required this.errorMessage,
|
||||||
|
required this.onRetry,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Center(
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
const Text(
|
||||||
|
'エラーが発生しました',
|
||||||
|
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
Text(errorMessage),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: onRetry,
|
||||||
|
child: const Text('再試行'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user