import 'package:flutter/material.dart'; import 'package:geolocator/geolocator.dart'; import 'package:get/get.dart'; import 'package:gifunavi/pages/destination/destination_controller.dart'; import 'package:gifunavi/pages/drawer/drawer_page.dart'; import 'package:gifunavi/pages/index/index_controller.dart'; import 'package:gifunavi/routes/app_pages.dart'; import 'package:gifunavi/widgets/list_widget.dart'; import 'package:gifunavi/widgets/map_widget.dart'; import 'package:gifunavi/utils/location_controller.dart'; import '../permission/permission.dart'; // index_page.dartファイルの主な内容です。 // このファイルは、アプリのメインページのUIを構築し、各機能へのナビゲーションを提供しています。 // また、IndexControllerとDestinationControllerを使用して、状態管理と各種機能の実装を行っています。 // // MapWidgetとListWidgetは、それぞれ別のファイルで定義されているウィジェットであり、マップモードとリストモードの表示を担当しています。 // // 全体的に、index_page.dartはアプリのメインページの構造を定義し、他のコンポーネントやページへの橋渡しを行っているファイルです。 // // 要検討:GPSデータの表示アイコンをタップした際のエラーハンドリングを追加することをお勧めします。 // MapWidgetとListWidgetの切り替えにObxを使用していますが、パフォーマンスを考慮して、必要な場合にのみウィジェットを再構築するようにしてください。 // DestinationControllerのisSimulationModeを使用してGPS信号の強弱をシミュレーションしていますが、本番環境では適切に実際のGPS信号を使用するようにしてください。 // IndexPageクラスは、GetViewを継承したStatelessWidgetです。このクラスは、アプリのメインページを表すウィジェットです。 // class IndexPage extends StatefulWidget { const IndexPage({super.key}); @override _IndexPageState createState() => _IndexPageState(); } class _IndexPageState extends State { final IndexController indexController = Get.find(); final DestinationController destinationController = Get.find(); @override void initState() { super.initState(); _checkPermissionAndInitialize(); } Future _checkPermissionAndInitialize() async { // 位置情報の許可が得られた場合の処理 await _initializeMap(); } Future _initializeMap() async { await indexController.loadLocations(); setState(() {}); // 状態を更新してUIを再構築 } void _showLocationServiceDisabledOrDeniedError() { Get.snackbar( 'エラー', '位置情報サービスが無効か、許可されていません。設定を確認してください。', duration: const Duration(seconds: 5), snackPosition: SnackPosition.BOTTOM, mainButton: TextButton( child: const Text('設定'), onPressed: () => Geolocator.openLocationSettings(), ), ); } void _showPermissionDeniedError() { Get.snackbar( 'エラー', '位置情報の許可が必要です。設定から許可してください。', duration: const Duration(seconds: 5), snackPosition: SnackPosition.BOTTOM, ); } void checkEventAndNavigate() async { if (indexController.currentUser.isNotEmpty && indexController.currentUser[0]["user"]["event_code"] == null) { // イベントコードがない場合、EVENT_ENTRYページに遷移 await Get.toNamed(AppPages.EVENT_ENTRY); // EVENT_ENTRYページから戻ってきた後に警告を表示 _showEventSelectionWarning(); } } void checkLoginAndShowDialog() async { if (indexController.currentUser.isEmpty) { showDialog( context: context, barrierDismissible: false, builder: (BuildContext context) { return AlertDialog( title: const Text('ログインが必要です'), content: const Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('1) ログインされていません。ロゲに参加するにはログインが必要です。'), SizedBox(height: 10), Text('2) ログイン後、個人情報入力、チーム登録、エントリー登録を行なってください。'), SizedBox(height: 10), Text('3) エントリー登録は場所と日にちごとに行なってください。'), ], ), actions: [ TextButton( child: const Text('キャンセル'), onPressed: () { Navigator.of(context).pop(); }, ), ElevatedButton( child: const Text('ログイン'), onPressed: () { Navigator.of(context).pop(); Get.toNamed(AppPages.LOGIN); }, ), ], ); }, ); }else{ if(indexController.currentUser[0]["user"]["event_code"] == null) { // イベントコードがない場合、EVENT_ENTRYページに遷移 await Get.toNamed(AppPages.EVENT_ENTRY); // EVENT_ENTRYページから戻ってきた後に警告を表示 _showEventSelectionWarning(); } } } void _showEventSelectionWarning() { Get.dialog( AlertDialog( title: const Text('警告'), content: const Text('イベントを選択してください。'), actions: [ TextButton( child: const Text('OK'), onPressed: () => Get.back(), ), ], ), ); } // class IndexPage extends GetView { // IndexPage({Key? key}) : super(key: key); // IndexControllerとDestinationControllerのインスタンスを取得しています。 // final LocationController locationController = Get.find(); // buildメソッドは、ウィジェットのUIを構築するメソッドです。 // ここでは、WillPopScopeウィジェットを使用して、端末の戻るボタンが押された際の動作を制御しています。 // @override Widget build(BuildContext context) { /* return PopScope( canPop: false, child: Scaffold( */ return Scaffold( // // Scaffoldウィジェットを使用して、アプリのメインページのレイアウトを構築しています。 // drawer: DrawerPage(), appBar: AppBar( title: Obx(() => Text(indexController.selectedEventName.value)), //title: Text("add_location".tr), actions: [ IconButton( onPressed: () async { Get.toNamed(AppPages.GPS); }, icon: const Icon(Icons.telegram)), IconButton( onPressed: () { Get.toNamed(AppPages.HISTORY); }, icon: const Icon(Icons.history)), IconButton( onPressed: () { final tk = indexController.currentUser[0]["token"]; if (tk != null) { destinationController.fixMapBound(tk); } }, icon: const Icon(Icons.refresh)), InkWell( onTap: () { Get.toNamed(AppPages.SEARCH); }, child: Container( height: 32, width: 75, decoration: BoxDecoration( color: Colors.blue, borderRadius: BorderRadius.circular(25), ), child: const Center( child: Icon(Icons.search), ), ), ), //CatWidget(indexController: indexController,), ], ), body: SafeArea( child:Obx(() { if (indexController.isLoadingLocations.value) { return const Center(child: CircularProgressIndicator()); // Index Controller } else { return indexController.mode.value == 0 ? const MapWidget() : const ListWidget(); } }), ), // マップモードとリストモードを切り替えるためのボタンです。 floatingActionButton: FloatingActionButton( onPressed: () { indexController.toggleMode(); }, elevation: 1.0, // // Obxウィジェットを使用して、indexController.mode.valueの値に基づいて、MapWidgetまたはListWidgetを表示しています。 // child: Obx( () => indexController.mode.value == 0 ? const Image(image: AssetImage('assets/images/list2.png')) : const Image(image: AssetImage('assets/images/map.png')), ), ), floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked, ); } }