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; import 'package:gifunavi/services/motion_service.dart'; Map deviceInfo = {}; /* void saveGameState() async { DestinationController destinationController = Get.find(); 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 saveGameState() async { DestinationController destinationController = Get.find(); IndexController indexController = Get.find(); 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 restoreGame() async { SharedPreferences pref = await SharedPreferences.getInstance(); IndexController indexController = Get.find(); 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.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(); DestinationController destinationController = Get.find(); 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(); if (Platform.isIOS) { // アプリの起動時にモーション更新を開始 await MotionService.startMotionUpdates(); } 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 initServices(); runApp(const ProviderScope(child: MyApp())); }catch(e, stackTrace){ print('Error during initialization: $e'); print('Stack trace: $stackTrace'); // エラーが発生した場合、エラー画面を表示 runApp(ErrorApp(error: e.toString())); } } Future initServices() async { print('Starting services ...'); try { //await Get.putAsync(() => ApiService().init()); await _initApiService(); debugPrint("1: start ApiService"); // コントローラーを初期化 Get.put(IndexController(apiService: Get.find()), permanent: true); Get.put(SettingsController(), permanent: true); Get.put(DestinationController(), permanent: true); Get.put(LocationController(), permanent: true); debugPrint("2: Controllers initialized"); /* // すべてのコントローラーとサービスを非同期で初期化 Get.lazyPut(() => IndexController(apiService: Get.find())); debugPrint("2: start IndexController"); // その他のコントローラーを遅延初期化 Get.lazyPut(() => SettingsController()); debugPrint("2: start SettingsController"); Get.lazyPut(() => DestinationController()); debugPrint("3: start DestinationController"); Get.lazyPut(() => LocationController()); debugPrint("4: start LocationController"); */ // 非同期処理を並列実行 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 _initLocationController() async { if (!Get.isRegistered()) { Get.put(LocationController()); } print('=== 1. LocationController started...'); } Future _initTimeZone() async { tz.initializeTimeZones(); } Future _initCacheProvider() async { await CacheProvider.initialize(); } Future _checkPermissions() async { await PermissionController.checkAndRequestPermissions(); } Future _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 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 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? positionStream; bool background=false; DateTime lastGPSCollectedTime=DateTime.now(); String team_name=""; String event_code=""; Future startBackgroundTracking() async { try { if (Platform.isIOS && background == false) { final IndexController indexController = Get.find(); 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 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 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 createState() => _MyAppState(); } class _MyAppState extends State 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; bool _isControllerInitialized = false; @override void initState() { super.initState(); _initializeControllers(); WidgetsBinding.instance.addObserver(this); //_startMemoryMonitoring(); if (context.mounted) { // _restoreGameAndInitialize(); } // ウィジェットが構築された後に権限をチェック WidgetsBinding.instance.addPostFrameCallback((_) { PermissionController.checkAndRequestPermissions(); }); debugPrint("Start MyAppState..."); } Future _restoreGameAndInitialize() async { await restoreGame(); // ここに他の初期化処理を追加できます } Future _initializeControllers() async { while (!Get.isRegistered() || !Get.isRegistered() || !Get.isRegistered() || !Get.isRegistered()) { await Future.delayed(const Duration(milliseconds: 100)); } if (!_isControllerInitialized) { _locationController = Get.find(); _indexController = Get.find(); _destinationController = Get.find(); _permissionController = Get.find(); _isControllerInitialized = true; } /* while (true) { try { _locationController = Get.find(); break; // DestinationControllerが見つかったらループを抜ける } catch (e) { // DestinationControllerがまだ利用可能でない場合は少し待ってから再試行 await Future.delayed(const Duration(milliseconds: 100)); } } */ /* if (!Get.isRegistered()) { _locationController = Get.put(LocationController(), permanent: true); } */ /* while (true) { try { _indexController = Get.find(); break; // DestinationControllerが見つかったらループを抜ける } catch (e) { // DestinationControllerがまだ利用可能でない場合は少し待ってから再試行 await Future.delayed(const Duration(milliseconds: 100)); } } */ /* if (!Get.isRegistered()) { _indexController = Get.put(IndexController(apiService: Get.find()), permanent: true); } */ /* while (true) { try { _destinationController = Get.find(); break; // DestinationControllerが見つかったらループを抜ける } catch (e) { // DestinationControllerがまだ利用可能でない場合は少し待ってから再試行 await Future.delayed(const Duration(milliseconds: 100)); } } */ /* if (!Get.isRegistered()) { _destinationController = Get.put(DestinationController(), permanent: true); } */ /* while (true) { try { _permissionController = Get.find(); break; // DestinationControllerが見つかったらループを抜ける } catch (e) { // DestinationControllerがまだ利用可能でない場合は少し待ってから再試行 await Future.delayed(const Duration(milliseconds: 100)); } } */ /* if (!Get.isRegistered()) { _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> _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: [ 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() { _isControllerInitialized = false; WidgetsBinding.instance.removeObserver(this); _memoryCheckTimer?.cancel(); super.dispose(); } // void saveGameState() async { // DestinationController destinationController = Get.find(); // 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 didChangeAppLifecycleState(AppLifecycleState state) async { try { if (!Get.isRegistered()) { _indexController = Get.find(); } if (!Get.isRegistered()) { _locationController = Get.find(); } if (!Get.isRegistered()) { _destinationController = Get.find(); } switch (state) { case AppLifecycleState.resumed: if (Platform.isIOS) { MotionService.startMotionUpdates(); } //await _onResumed(); await _onResumed(); break; case AppLifecycleState.inactive: // アプリが非アクティブになったときに発生します。 await _onInactive(); break; case AppLifecycleState.paused: MotionService.stopMotionUpdates(); // バックグラウンドに移行したときの処理 //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 _onResumed() async { debugPrint("==(Status Changed)==> RESUMED"); try { if (!_isControllerInitialized) { await _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 _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 _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 _onDetached() async { debugPrint(" ==(Status Changed)==> DETACHED アプリは終了します。"); await saveGameState(); // アプリ終了時の追加処理 } Future _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, ); } }