Files
rog_app/lib/main.dart
2024-09-02 21:25:19 +09:00

837 lines
29 KiB
Dart
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import 'dart:async';
import 'dart:io';
//import 'dart:convert';
//import 'dart:developer';
import 'package:gifunavi/model/gps_data.dart';
//import 'package:gifunavi/pages/home/home_page.dart';
import 'package:gifunavi/utils/database_gps.dart';
import 'package:flutter/material.dart';
//import 'package:flutter_map_tile_caching/flutter_map_tile_caching.dart';
import 'package:geolocator/geolocator.dart';
import 'package:get/get.dart';
//import 'package:vm_service/vm_service.dart';
//import 'package:dart_vm_info/dart_vm_info.dart';
import 'package:timezone/data/latest.dart' as tz;
import 'package:gifunavi/pages/settings/settings_controller.dart';
import 'package:gifunavi/pages/destination/destination_controller.dart';
import 'package:gifunavi/pages/index/index_binding.dart';
import 'package:gifunavi/pages/index/index_controller.dart';
import 'package:gifunavi/routes/app_pages.dart';
import 'package:gifunavi/utils/location_controller.dart';
import 'package:gifunavi/utils/string_values.dart';
import 'package:gifunavi/widgets/debug_widget.dart';
import 'package:shared_preferences/shared_preferences.dart';
// import 'package:is_lock_screen/is_lock_screen.dart';
//import 'package:gifunavi/services/device_info_service.dart';
import 'package:gifunavi/services/error_service.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
//import 'dart:async';
//import 'package:get/get.dart';
import 'package:flutter/services.dart';
import 'package:permission_handler/permission_handler.dart';
import 'pages/permission/permission.dart';
import 'package:gifunavi/services/api_service.dart';
import 'package:gifunavi/provider/cached_tile_provider.dart';
//import 'package:gifunavi/pages/entry/entry_controller.dart';
//import 'package:gifunavi/pages/team/team_controller.dart';
import 'package:timezone/timezone.dart' as tz;
Map<String, dynamic> deviceInfo = {};
/*
void saveGameState() async {
DestinationController destinationController =
Get.find<DestinationController>();
SharedPreferences pref = await SharedPreferences.getInstance();
pref.setBool("is_in_rog", destinationController.isInRog.value);
pref.setBool(
"rogaining_counted", destinationController.rogainingCounted.value);
pref.setBool("ready_for_goal", DestinationController.ready_for_goal);
}
*/
// 現在のユーザーのIDも一緒に保存するようにします。
Future<void> saveGameState() async {
DestinationController destinationController =
Get.find<DestinationController>();
IndexController indexController = Get.find<IndexController>();
SharedPreferences pref = await SharedPreferences.getInstance();
debugPrint("ゲームステータス保存  = ${indexController.currentUser}");
if(indexController.currentUser.isNotEmpty) {
pref.setInt("user_id", indexController.currentUser[0]["user"]["id"]);
if(indexController.currentUser[0]["user"]["event_date"]!=null) {
final date = indexController.currentUser[0]["user"]["event_date"];
pref.setString('eventDate', date.toIso8601String());
debugPrint("Saved date is ${date} => ${date.toIso8601String()}");
pref.setString('eventCode', indexController.currentUser[0]["user"]["event_code"]);
pref.setString('teamName', indexController.currentUser[0]["user"]["team_name"]);
pref.setString('group', indexController.currentUser[0]["user"]["group"]);
//final zekken = indexController.currentUser[0]["user"]["zekken_number"];
pref.setInt('zekkenNumber', indexController.currentUser[0]["user"]["zekken_number"]);
}
}else{
debugPrint("User is empty....");
}
pref.setBool("is_in_rog", destinationController.isInRog.value);
pref.setBool(
"rogaining_counted", destinationController.rogainingCounted.value);
pref.setBool("ready_for_goal", DestinationController.ready_for_goal);
}
// _indexController.currentUser[0]["user"]["event_date"] = entryDate; // 追加2024-8-9
// _indexController.currentUser[0]["user"]["event_code"] = entry.event.eventName;
// _indexController.currentUser[0]["user"]["team_name"] = entry.team.teamName;
// _indexController.currentUser[0]["user"]["group"] = entry.team.category.categoryName;
// _indexController.currentUser[0]["user"]["zekken_number"] = entry.zekkenNumber;
Future<void> restoreGame() async {
SharedPreferences pref = await SharedPreferences.getInstance();
IndexController indexController = Get.find<IndexController>();
int? savedUserId = pref.getInt("user_id");
//int? currUserId = indexController.currentUser[0]['user']['id'];
debugPrint("ゲームステータス再現  savedUserId=${savedUserId}");
if (indexController.currentUser.isNotEmpty &&
indexController.currentUser[0]["user"]["id"] == savedUserId) {
final dateString = pref.getString('eventDate');
if (dateString != null) {
final parsedDate = DateTime.parse(dateString);
final jstDate = tz.TZDateTime.from(parsedDate, tz.getLocation('Asia/Tokyo'));
debugPrint("restore date is ${dateString} => ${jstDate}");
indexController.currentUser[0]["user"]["event_date"] = jstDate;
//indexController.currentUser[0]["user"]["event_date"] = DateTime.parse(dateString);
}
//debugPrint("restore date is ${dateString?} => ${DateTime.parse(dateString)}");
indexController.currentUser[0]["user"]["event_code"] = pref.getString('eventCode');
indexController.currentUser[0]["user"]["team_name"] = pref.getString('teamName');
indexController.currentUser[0]["user"]["group"] = pref.getString('group');
indexController.currentUser[0]["user"]["zekken_number"] = pref.getInt('zekkenNumber');
debugPrint("user = ${indexController.currentUser[0]["user"]}");
DestinationController destinationController = Get.find<DestinationController>();
destinationController.skipGps = false;
destinationController.isInRog.value = pref.getBool("is_in_rog") ?? false;
destinationController.rogainingCounted.value = pref.getBool("rogaining_counted") ?? false;
DestinationController.ready_for_goal = pref.getBool("ready_for_goal") ?? false;
//await Get.putAsync(() => ApiService().init());
if (indexController.currentUser[0]["user"]["event_code"] != null) {
indexController.setSelectedEventName(indexController.currentUser[0]["user"]["event_code"]);
} else {
indexController.setSelectedEventName('未参加');
}
}else{
indexController.setSelectedEventName('未参加');
}
}
/*
void restoreGame_new() async {
SharedPreferences pref = await SharedPreferences.getInstance();
IndexController indexController = Get.find<IndexController>();
DestinationController destinationController = Get.find<DestinationController>();
destinationController.skipGps = false;
destinationController.isInRog.value = pref.getBool("is_in_rog") ?? false;
destinationController.rogainingCounted.value = pref.getBool("rogaining_counted") ?? false;
DestinationController.ready_for_goal = pref.getBool("ready_for_goal") ?? false;
int? savedUserId = pref.getInt("user_id");
if (indexController.currentUser.isNotEmpty && indexController.currentUser[0]["user"]["id"] == savedUserId) {
final dateString = pref.getString('eventDate');
if (dateString != null) {
indexController.currentUser[0]["user"]["event_date"] = DateTime.parse(dateString);
}
indexController.currentUser[0]["user"]["event_code"] = pref.getString('eventCode');
indexController.currentUser[0]["user"]["team_name"] = pref.getString('teamName');
indexController.currentUser[0]["user"]["group"] = pref.getString('group');
indexController.currentUser[0]["user"]["zekken_number"] = pref.getInt('zekkenNumber');
if (indexController.currentUser[0]["user"]["event_code"] != null) {
indexController.setSelectedEventName(indexController.currentUser[0]["user"]["event_code"]);
} else {
indexController.setSelectedEventName('未参加');
_showEventSelectionWarning();
Get.toNamed(AppPages.EVENT_ENTRY);
}
} else {
indexController.setSelectedEventName('未参加');
_showEventSelectionWarning();
Get.toNamed(AppPages.EVENT_ENTRY);
}
await Get.putAsync(() => ApiService().init());
}
*/
void _showEventSelectionWarning() {
Get.dialog(
AlertDialog(
title: Text('警告'),
content: Text('イベントを選択してください。'),
actions: [
TextButton(
child: Text('OK'),
onPressed: () => Get.back(),
),
],
),
);
}
void main() async {
WidgetsFlutterBinding.ensureInitialized();
final IndexController _indexController;
FlutterError.onError = (FlutterErrorDetails details) {
FlutterError.presentError(details);
Get.log('Flutter error: ${details.exception}');
Get.log('Stack trace: ${details.stack}');
ErrorService.reportError(details.exception, details.stack ?? StackTrace.current, deviceInfo, LogManager().operationLogs);
};
try {
//await Get.putAsync(() => ApiService().init());
await _initApiService();
debugPrint("1: start ApiService");
// すべてのコントローラーとサービスを非同期で初期化
Get.lazyPut(() => IndexController(apiService: Get.find<ApiService>()));
debugPrint("2: start IndexController");
// その他のコントローラーを遅延初期化
Get.lazyPut(() => SettingsController());
debugPrint("2: start SettingsController");
Get.lazyPut(() => DestinationController());
debugPrint("3: start DestinationController");
await initServices();
runApp(const ProviderScope(child: MyApp()));
}catch(e, stackTrace){
print('Error during initialization: $e');
print('Stack trace: $stackTrace');
// エラーが発生した場合、エラー画面を表示
runApp(ErrorApp(error: e.toString()));
}
}
Future<void> initServices() async {
print('Starting services ...');
try {
// 非同期処理を並列実行
await Future.wait([
_initTimeZone(),
_initCacheProvider(),
]);
print('=== 5. Initialized TimeZone...');
print('=== 6. CacheProvider started...');
Get.put(PermissionController());
await _checkPermissions();
debugPrint("7: start PermissionController");
}catch(e){
print('Error initializing : $e');
}
print('All services started...');
}
Future<void> _initLocationController() async {
if (!Get.isRegistered<LocationController>()) {
Get.put(LocationController());
}
print('=== 1. LocationController started...');
}
Future<void> _initTimeZone() async {
tz.initializeTimeZones();
}
Future<void> _initCacheProvider() async {
await CacheProvider.initialize();
}
Future<void> _checkPermissions() async {
await PermissionController.checkAndRequestPermissions();
}
Future<void> _initApiService() async {
await Get.putAsync(() => ApiService().init());
//Get.lazyPut(() => ApiService());
}
class ErrorApp extends StatelessWidget {
final String error;
const ErrorApp({super.key, required this.error});
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Center(
child: Text('アプリの起動中にエラーが発生しました: $error'),
),
),
);
}
}
Future<void> requestLocationPermission() async {
try {
final status = await Permission.locationAlways.request();
if (status == PermissionStatus.granted) {
print('Location permission granted');
} else {
print('Location permission denied');
//await showLocationPermissionDeniedDialog(); // 追加
}
} catch (e) {
print('Error requesting location permission: $e');
}
}
// メモリ使用量の解説https://qiita.com/hukusuke1007/items/e4e987836412e9bc73b9
/*
// 2024-4-8 Akira: メモリ使用量のチェックのため追加 See #2810
// startMemoryMonitoring関数が5分ごとに呼び出され、メモリ使用量をチェックします。
// メモリ使用量が閾値ここでは500MBを超えた場合、エラーメッセージとスタックトレースをレポートします。
//
void startMemoryMonitoring() {
const threshold = 500 * 1024 * 1024; // 500MB
// メモリ使用量情報を取得
final memoryUsage = MemoryUsage.fromJson(DartVMInfo.getAllocationProfile());
if (memoryUsage.heapUsage > threshold) {
final now = DateTime.now().toIso8601String();
final message = 'High memory usage detected at $now: ${memoryUsage.heapUsage} bytes';
print(message);
reportError(message, StackTrace.current);
showMemoryWarningDialog();
}
Timer(const Duration(minutes: 5), startMemoryMonitoring);
}
class MemoryUsage {
final int heapUsage;
MemoryUsage({required this.heapUsage});
factory MemoryUsage.fromJson(Map<String, dynamic> json) {
return MemoryUsage(
heapUsage: json['heapUsage'] as int,
);
}
}
*/
// 2024-4-8 Akira: メモリ使用量のチェックのため追加 See #2810
// reportError関数でエラーレポートを送信します。具体的な実装は、利用するエラー報告サービスによって異なります。
void reportError(String message, StackTrace stackTrace) async {
// エラーレポートの送信処理を実装
// 例: SentryやFirebase Crashlyticsなどのエラー報告サービスを利用
print("ReportError : $message . stacktrace : $stackTrace");
}
// 2024-4-8 Akira: メモリ使用量のチェックのため追加 See #2810
// showMemoryWarningDialog関数で、メモリ使用量が高い場合にユーザーに警告ダイアログを表示します。
//
void showMemoryWarningDialog() {
if (Get.context != null) {
showDialog(
context: Get.context!,
builder: (context) => AlertDialog(
title: const Text('メモリ使用量の警告'),
content: const Text('アプリのメモリ使用量が高くなっています。アプリを再起動することをお勧めします。'),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: const Text('OK'),
),
],
),
);
}
}
StreamSubscription<Position>? positionStream;
bool background=false;
DateTime lastGPSCollectedTime=DateTime.now();
String team_name="";
String event_code="";
Future<void> startBackgroundTracking() async {
try {
if (Platform.isIOS && background == false) {
final IndexController indexController = Get.find<IndexController>();
if (indexController.currentUser.isNotEmpty &&
indexController.currentUser[0]["user"]['team_name'] != null) {
team_name = indexController.currentUser[0]["user"]['team_name'];
event_code = indexController.currentUser[0]["user"]["event_code"];
}
background = true;
debugPrint("バックグラウンド処理を開始しました。");
const LocationSettings locationSettings = LocationSettings(
accuracy: LocationAccuracy.high,
distanceFilter: 100,
);
try {
positionStream =
Geolocator.getPositionStream(locationSettings: locationSettings)
.listen((Position? position) async {
if (position != null) {
final lat = position.latitude;
final lng = position.longitude;
//final timestamp = DateTime.now();
final accuracy = position.accuracy;
// GPS信号強度がlowの場合はスキップ
if (accuracy > 100) {
debugPrint(
"GPS signal strength is low. Skipping data saving.");
return;
}
Duration difference = lastGPSCollectedTime.difference(
DateTime.now())
.abs();
// 最後にGPS信号を取得した時刻から10秒以上経過、かつ10m以上経過普通に歩くスピード
//debugPrint("時間差:${difference}");
if (difference.inSeconds >= 10) {
debugPrint(
"バックグラウンドでのGPS取得時の処理(10secおき) count=${difference
.inSeconds}, time=${DateTime.now()}");
// DBにGPSデータを保存 pages/destination/destination_controller.dart
await addGPStoDB(lat, lng);
lastGPSCollectedTime = DateTime.now();
}
}
}, onError: (error) {
if (error is LocationServiceDisabledException) {
print('Location services are disabled');
} else if (error is PermissionDeniedException) {
print('Location permissions are denied');
} else {
print('Location Error: $error');
}
});
} catch (e) {
print('Error starting background tracking: $e');
}
} else if (Platform.isAndroid && background == false) {
background = true;
debugPrint("バックグラウンド処理を開始しました。");
try {
// 位置情報の権限が許可されているかを確認
await PermissionController.checkAndRequestPermissions();
} catch (e) {
print('Error starting background tracking: $e');
}
}
} catch (e) {
print('Error starting background tracking: $e');
// 再試行するか、エラーを適切に処理
}
}
Future<void> addGPStoDB(double la, double ln) async {
//debugPrint("in addGPStoDB ${indexController.currentUser}");
GpsDatabaseHelper db = GpsDatabaseHelper.instance;
try {
GpsData gpsData = GpsData(
id: 0,
team_name: team_name,
event_code: event_code,
lat: la,
lon: ln,
is_checkin: 0,
created_at: DateTime.now().millisecondsSinceEpoch);
var res = await db.insertGps(gpsData);
debugPrint("バックグラウンドでのGPS保存");
} catch (err) {
print("errr ready gps $err");
return;
}
}
Future<void> stopBackgroundTracking() async {
try {
if (Platform.isIOS && background == true) {
background = false;
debugPrint("バックグラウンド処理:停止しました。");
await positionStream?.cancel();
positionStream = null;
} else if (Platform.isAndroid && background == true) {
background = false;
debugPrint("バックグラウンド処理:停止しました。");
const platform = MethodChannel('location');
try {
await platform.invokeMethod('stopLocationService');
} on PlatformException catch (e) {
print("Failed to stop location service: '${e.message}'.");
}
}
} catch(e){
print('Error stopping background tracking: $e');
}
}
class MyApp extends StatefulWidget {
const MyApp({super.key});
@override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> with WidgetsBindingObserver {
// This widget is the root of your application.
late final LocationController _locationController;
late final IndexController _indexController;
late final DestinationController _destinationController;
late final PermissionController _permissionController;
Timer? _memoryCheckTimer;
@override
void initState() {
super.initState();
_initializeControllers();
WidgetsBinding.instance.addObserver(this);
//_startMemoryMonitoring();
if (context.mounted) {
// _restoreGameAndInitialize();
}
// ウィジェットが構築された後に権限をチェック
WidgetsBinding.instance.addPostFrameCallback((_) {
PermissionController.checkAndRequestPermissions();
});
debugPrint("Start MyAppState...");
}
Future<void> _restoreGameAndInitialize() async {
await restoreGame();
// ここに他の初期化処理を追加できます
}
void _initializeControllers() {
/*
if (!Get.isRegistered<IndexController>()) {
_locationController = Get.put(LocationController(), permanent: true);
}
if (!Get.isRegistered<IndexController>()) {
_indexController = Get.put(IndexController(apiService: Get.find<ApiService>()), permanent: true);
}
if (!Get.isRegistered<DestinationController>()) {
_destinationController =
Get.put(DestinationController(), permanent: true);
}
if (!Get.isRegistered<PermissionController>()) {
_permissionController = Get.put(PermissionController());
}
// 他の必要なコントローラーの初期化
*/
}
void _startMemoryMonitoring() {
/*
_memoryCheckTimer = Timer.periodic(const Duration(seconds: 10), (timer) {
_checkMemoryUsage();
});
*/
}
void _checkMemoryUsage() async {
final memoryInfo = await _getMemoryInfo();
//debugPrint('Current memory usage: ${memoryInfo['used']} MB');
if (memoryInfo['used']! > 100) { // 100MB以上使用している場合
_performMemoryCleanup();
}
}
Future<Map<String, int>> _getMemoryInfo() async {
// プラットフォーム固有のメモリ情報取得ロジックを実装
// この例では仮の値を返しています
return {'total': 1024, 'used': 512};
}
void _performMemoryCleanup() {
/*
debugPrint('Performing memory cleanup');
// キャッシュのクリア
Get.deleteAll(force: false); // 永続的なコントローラーを除外してキャッシュをクリア
imageCache.clear();
imageCache.clearLiveImages();
// 大きなオブジェクトの解放
_clearLargeObjects();
// 未使用のリソースの解放
_releaseUnusedResources();
// ガベージコレクションの促進
_forceGarbageCollection();
debugPrint('Performing memory cleanup');
*/
}
void _clearLargeObjects() {
// 大きなリストやマップをクリア
// 例: myLargeList.clear();
}
void _releaseUnusedResources() {
// 使用していないストリームのクローズ
// 例: myStream?.close();
}
void _forceGarbageCollection() {
/*
Timer(const Duration(seconds: 1), () {
debugPrint('Forcing garbage collection');
// ignore: dead_code
bool didRun = false;
assert(() {
didRun = true;
return true;
}());
if (didRun) {
debugPrint('Garbage collection forced in debug mode');
}
});
*/
}
/*
void showPermissionRequiredDialog() {
showDialog(
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
return AlertDialog(
title: Text('権限が必要です'),
content: Text('このアプリは機能するために位置情報の権限が必要です。設定で権限を許可してください。'),
actions: <Widget>[
TextButton(
child: Text('設定を開く'),
onPressed: () {
openAppSettings();
Navigator.of(context).pop();
},
),
TextButton(
child: Text('アプリを終了'),
onPressed: () {
// アプリを終了
Navigator.of(context).pop();
// よりクリーンな終了のために 'flutter_exit_app' のようなプラグインを使用することをお勧めします
// 今回は単にすべてのルートをポップします
Navigator.of(context).popUntil((route) => false);
},
),
],
);
},
);
}
*/
@override
void dispose() {
WidgetsBinding.instance.removeObserver(this);
_memoryCheckTimer?.cancel();
super.dispose();
}
// void saveGameState() async {
// DestinationController destinationController = Get.find<DestinationController>();
// SharedPreferences pref = await SharedPreferences.getInstance();
// pref.setBool("is_in_rog", destinationController.is_in_rog.value);
// pref.setBool("rogaining_counted", destinationController.rogaining_counted.value);
// }
@override
Future<void> didChangeAppLifecycleState(AppLifecycleState state) async {
try {
if (!Get.isRegistered<IndexController>()) {
_indexController = Get.find<IndexController>();
}
if (!Get.isRegistered<LocationController>()) {
_locationController = Get.find<LocationController>();
}
if (!Get.isRegistered<DestinationController>()) {
_destinationController = Get.find<DestinationController>();
}
switch (state) {
case AppLifecycleState.resumed:
//await _onResumed();
await _onResumed();
break;
case AppLifecycleState.inactive:
// アプリが非アクティブになったときに発生します。
await _onInactive();
break;
case AppLifecycleState.paused:
// バックグラウンドに移行したときの処理
//locationController.resumePositionStream();
await _onPaused();
break;
case AppLifecycleState.detached:
// アプリが終了する直前に発生します。この状態では、アプリはメモリから解放される予定です。
//locationController.resumePositionStream();
await _onDetached();
break;
case AppLifecycleState.hidden:
// Web用の特殊な状態で、モバイルアプリでは発生しません。
//locationController.resumePositionStream();
await _onHidden();
break;
}
}catch(e){
print('Error finding didChangeAppLifecycleState: $e');
_initializeControllers();
}
}
Future<void> _onResumed() async {
debugPrint("==(Status Changed)==> RESUMED");
try {
_initializeControllers();
await stopBackgroundTracking();
_destinationController.restartGPS();
if (Platform.isIOS && _destinationController.isRunningBackgroundGPS) {
_locationController.resumePositionStream();
await restoreGame();
_destinationController.isRunningBackgroundGPS = false;
} else if (Platform.isAndroid) {
if (_destinationController.isRunningBackgroundGPS) {
const platform = MethodChannel('location');
await platform.invokeMethod('stopLocationService');
_destinationController.isRunningBackgroundGPS = false;
_destinationController.restartGPS();
}
_locationController.resumePositionStream();
await restoreGame();
}
} catch(e) {
print('Error in _onResumed: $e');
// 必要に応じて再試行またはエラー処理
}
}
Future<void> _onInactive() async {
debugPrint("==(Status Changed)==> INACTIVE");
if (Platform.isIOS && !_destinationController.isRunningBackgroundGPS) {
debugPrint(" ==(Status Changed)==> INACTIVE. 非アクティブ処理。");
_locationController.stopPositionStream();
_destinationController.isRunningBackgroundGPS = true;
await startBackgroundTracking();
} else if (Platform.isAndroid && !_destinationController.isRunningBackgroundGPS) {
// Android特有の処理があれば追加
debugPrint(" ==(Status Changed)==> INACTIVE. 非アクティブ処理。");
}else{
debugPrint("==(Status Changed)==> INACTIVE 不明状態");
}
await saveGameState();
}
Future<void> _onPaused() async {
debugPrint(" ==(Status Changed)==> PAUSED. バックグラウンド処理。");
if (Platform.isAndroid && !_destinationController.isRunningBackgroundGPS) {
debugPrint(" ==(Status Changed)==> PAUSED. Android バックグラウンド処理。");
_locationController.stopPositionStream();
const platform = MethodChannel('location');
await platform.invokeMethod('startLocationService');
_destinationController.isRunningBackgroundGPS = true;
}
await saveGameState();
}
Future<void> _onDetached() async {
debugPrint(" ==(Status Changed)==> DETACHED アプリは終了します。");
await saveGameState();
// アプリ終了時の追加処理
}
Future<void> _onHidden() async {
debugPrint(" ==(Status Changed)==> Hidden アプリが隠れた");
await saveGameState();
}
@override
Widget build(BuildContext context) {
return GetMaterialApp(
translations: StringValues(),
locale: const Locale('ja', 'JP'),
//locale: const Locale('en', 'US'),
fallbackLocale: const Locale('en', 'US'),
title: 'ROGAINING',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(
seedColor: const Color.fromARGB(255, 36, 135, 221)),
useMaterial3: true,
),
debugShowCheckedModeBanner: false,
defaultTransition: Transition.cupertino,
opaqueRoute: Get.isOpaqueRouteDefault,
popGesture: Get.isPopGestureEnable,
transitionDuration: const Duration(milliseconds: 230),
initialBinding: IndexBinding(), //HomeBinding(),
initialRoute: AppPages.PERMISSION,
getPages: AppPages.routes,
enableLog: true,
);
}
}