import 'dart:async'; import 'dart:typed_data'; import 'dart:io'; import 'package:alp_animated_splashscreen/alp_animated_splashscreen.dart'; import 'package:firebase_core/firebase_core.dart'; import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_local_notifications/flutter_local_notifications.dart'; import 'package:get/get.dart'; import 'package:nearledaily/constants/color_constants.dart'; import 'package:nearledaily/view/authentication/app_update_view.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:webview_flutter/webview_flutter.dart'; import 'package:webview_flutter_android/webview_flutter_android.dart'; import 'package:package_info_plus/package_info_plus.dart'; import 'package:new_version_plus/new_version_plus.dart'; import 'constants/api_constants.dart'; import 'controllers/account_controller/faq_controller.dart'; import 'controllers/authentication/auth_controller.dart'; import 'controllers/cart_controller/cart.dart'; import 'controllers/dashboard_controller/dashboard_controller.dart'; import 'controllers/intro_controller/intro_screen_controller.dart'; import 'controllers/order_controller/create_order_controller.dart'; import 'controllers/tenant/get_tenant.dart'; import 'controllers/tenant_controller /tenant_list.dart'; import 'helper/firebase_options.dart'; import 'service/bindings.dart'; import 'service/device_info/device_info.dart'; import 'view/home_view.dart'; import 'view/splash_view/splash_view.dart'; // ------------------------- // FIREBASE BACKGROUND HANDLER // ------------------------- @pragma('vm:entry-point') Future _firebaseMessagingBackgroundHandler(RemoteMessage message) async { await Firebase.initializeApp( options: DefaultFirebaseOptions.currentPlatform, ); final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin(); const AndroidInitializationSettings androidSettings = AndroidInitializationSettings('@mipmap/ic_launcher'); await flutterLocalNotificationsPlugin.initialize( const InitializationSettings(android: androidSettings), ); final androidPlugin = flutterLocalNotificationsPlugin .resolvePlatformSpecificImplementation(); await androidPlugin?.createNotificationChannel( const AndroidNotificationChannel( 'nearle_channel', 'Nearle Notifications', importance: Importance.max, ), ); final data = message.data; final title = data['title'] as String?; final body = data['body'] as String?; if (title != null && title.isNotEmpty) { await flutterLocalNotificationsPlugin.show( 999, title, body?.isNotEmpty == true ? body : null, NotificationDetails( android: AndroidNotificationDetails( 'nearle_channel', 'Nearle Notifications', importance: Importance.max, priority: Priority.high, fullScreenIntent: true, playSound: true, enableVibration: true, largeIcon: const DrawableResourceAndroidBitmap('nearle_logo.jpeg'), ), ), ); } } // ------------------------- // LOCAL NOTIFICATIONS SETUP // ------------------------- final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin(); Future _setupLocalNotifications() async { // Android settings const AndroidInitializationSettings initializationSettingsAndroid = AndroidInitializationSettings('@mipmap/ic_launcher'); // iOS/macOS settings const DarwinInitializationSettings initializationSettingsIOS = DarwinInitializationSettings( requestSoundPermission: true, requestBadgePermission: true, requestAlertPermission: true, ); // Combine all platforms const InitializationSettings initializationSettings = InitializationSettings( android: initializationSettingsAndroid, iOS: initializationSettingsIOS, macOS: initializationSettingsIOS, ); await flutterLocalNotificationsPlugin.initialize(initializationSettings); // Android-only notification channel if (!kIsWeb && defaultTargetPlatform == TargetPlatform.android) { const AndroidNotificationChannel channel = AndroidNotificationChannel( 'nearle_channel', 'Nearle Notifications', description: 'High priority notifications', importance: Importance.max, playSound: true, enableVibration: true, ); await flutterLocalNotificationsPlugin .resolvePlatformSpecificImplementation() ?.createNotificationChannel(channel); } } // ------------------------- // ROUTE OBSERVER // ------------------------- final RouteObserver> routeObserver = RouteObserver>(); // ------------------------- // APP VERSION CHECK // ------------------------- Future _printAndCheckAppVersions() async { try { final PackageInfo packageInfo = await PackageInfo.fromPlatform(); final String currentVersion = packageInfo.version; final String buildNumber = packageInfo.buildNumber; final String packageName = packageInfo.packageName; print("=================================================="); print("CURRENT APP VERSION: $currentVersion"); print("BUILD NUMBER: $buildNumber"); print("PACKAGE NAME: $packageName"); print("=================================================="); final prefs = await SharedPreferences.getInstance(); await prefs.setString('currentAppVersion', currentVersion); if (defaultTargetPlatform == TargetPlatform.android) { final newVersion = NewVersionPlus(androidId: "com.nearle.gear"); final status = await newVersion.getVersionStatus(); if (status != null) { print("PLAY STORE VERSION: ${status.storeVersion}"); print("CAN UPDATE: ${status.canUpdate}"); print("APP STORE LINK: ${status.appStoreLink}"); print("=================================================="); await prefs.setString('storeAppVersion', status.storeVersion); await prefs.setBool('updateAvailable', status.canUpdate); } else { print("Could not fetch Play Store version"); await prefs.setString('storeAppVersion', 'Unknown'); } } } catch (e) { print("Version check error: $e"); } } // Returns true if update available Future _checkForAppUpdate() async { if (defaultTargetPlatform != TargetPlatform.android) return false; try { final newVersion = NewVersionPlus(androidId: "com.nearle.gear"); final status = await newVersion.getVersionStatus(); if (status != null && status.canUpdate) { print("UPDATE DETECTED! Redirecting to AppUpdateView"); print("Current: ${status.localVersion} → Store: ${status.storeVersion}"); return true; } } catch (e) { print("Version check failed: $e"); } return false; } // ------------------------- // HANDLE NOTIFICATION TAP // ------------------------- void _handleMessage(RemoteMessage message) { print("Notification tapped: ${message.data}"); } // ------------------------- // ANIMATED SPLASH // ------------------------- class AnimatedSplashWithNavigation extends StatefulWidget { final Widget nextScreen; const AnimatedSplashWithNavigation({super.key, required this.nextScreen}); @override State createState() => _AnimatedSplashWithNavigationState(); } class _AnimatedSplashWithNavigationState extends State { @override void initState() { super.initState(); Future.delayed(const Duration(milliseconds: 5200), () { if (mounted) { Get.offAll(() => widget.nextScreen); } }); } @override Widget build(BuildContext context) { return AnimatedSplashScreen( logo: 'assets/images/splashimg.png', brandname: 'Nearle Daily', backgroundcolor: Colors.white, foregroundcolor: ColorConstants.primaryColor, brandnamecolor: ColorConstants.primaryColor, ); } } // ------------------------- // MAIN ENTRY POINT // ------------------------- Future main() async { WidgetsFlutterBinding.ensureInitialized(); // Firebase initialize with options (iOS/web safe) await Firebase.initializeApp( options: DefaultFirebaseOptions.currentPlatform, ); await _printAndCheckAppVersions(); await _setupLocalNotifications(); FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler); SystemChrome.setSystemUIOverlayStyle( SystemUiOverlayStyle(statusBarColor: Colors.grey[200]), ); final prefs = await SharedPreferences.getInstance(); prefs.setBool("firstTime", true); // ------------------------- // NOTIFICATION PERMISSIONS & FCM TOKEN // ------------------------- if (!kIsWeb) { if (Platform.isIOS) { NotificationSettings settings = await FirebaseMessaging.instance.requestPermission( alert: true, badge: true, sound: true, ); if (settings.authorizationStatus == AuthorizationStatus.authorized) { print('User granted permission'); // Listen for token refresh (APNs ready) FirebaseMessaging.instance.onTokenRefresh.listen((newToken) async { print('FCM Token (iOS APNs ready): $newToken'); await prefs.setString('fcmToken', newToken); }); // Optional: small delay initial attempt Future.delayed(const Duration(seconds: 2), () async { String? token = await FirebaseMessaging.instance.getToken(); print('FCM Token initial attempt (iOS): $token'); if (token != null) await prefs.setString('fcmToken', token); }); } else { print('User declined notification permission'); } } else { // Android: get token immediately String? token = await FirebaseMessaging.instance.getToken(); print('FCM Token (Android): $token'); if (token != null) await prefs.setString('fcmToken', token); } } // Foreground notifications FirebaseMessaging.onMessage.listen((RemoteMessage message) async { final data = message.data; final title = data['title'] ?? message.notification?.title ?? 'Nearle'; final body = data['body'] ?? message.notification?.body ?? ''; final ByteData jpegData = await rootBundle.load('assets/images/nearledaily.png'); final Uint8List jpegBytes = jpegData.buffer.asUint8List(); await flutterLocalNotificationsPlugin.show( DateTime.now().millisecondsSinceEpoch ~/ 1000, title, body, NotificationDetails( android: AndroidNotificationDetails( 'nearle_channel', 'Nearle Notifications', importance: Importance.max, priority: Priority.high, fullScreenIntent: true, playSound: true, enableVibration: true, largeIcon: ByteArrayAndroidBitmap(jpegBytes), ), iOS: const DarwinNotificationDetails(), ), ); }); // Android WebView Controller if (defaultTargetPlatform == TargetPlatform.android) { final params = PlatformWebViewControllerCreationParams(); AndroidWebViewController(params); } // API constants ApiConstants.tenantCustomers = ApiConstants.tenantCustomerLive; ApiConstants.orderedtenantCustomers = ApiConstants.orderedtenantCustomerLive; ApiConstants.login = ApiConstants.loginLive; // Controllers Get.put(TenantController(), permanent: true); Get.lazyPut(() => AuthController(), fenix: true); Get.lazyPut(() => DashboardController(), fenix: true); Get.lazyPut(() => CartController(), fenix: true); Get.lazyPut(() => BottomNavController(), fenix: true); Get.lazyPut(() => OrderedTenantController(), fenix: true); Get.lazyPut(() => OrderController(), fenix: true); Get.lazyPut(() => FaqController(), fenix: true); Get.lazyPut(() => IntroScreenController(), fenix: true); DeviceInfo deviceInfo = DeviceInfo(); await deviceInfo.getDeviceInfo(); await SystemChrome.setPreferredOrientations([ DeviceOrientation.portraitUp, DeviceOrientation.portraitDown, ]); final int? customerId = prefs.getInt('customerId'); final String? contactNo = prefs.getString('contactno'); bool updateAvailable = await _checkForAppUpdate(); Widget nextScreen; if (updateAvailable) { nextScreen = const AppUpdateView(); } else if (customerId != null && contactNo != null && contactNo.isNotEmpty) { nextScreen = BottomNavigation(); } else { nextScreen = const SplashScreenView(); } SystemChrome.setSystemUIOverlayStyle( const SystemUiOverlayStyle( statusBarColor: Colors.white, statusBarIconBrightness: Brightness.dark, statusBarBrightness: Brightness.light, ), ); runApp(MyApp(startScreen: nextScreen)); } // ------------------------- // MAIN APP // ------------------------- class MyApp extends StatelessWidget { final Widget startScreen; const MyApp({super.key, required this.startScreen}); @override Widget build(BuildContext context) { return GetMaterialApp( debugShowCheckedModeBanner: false, title: 'Nearle Daily', navigatorObservers: [routeObserver], theme: ThemeData( fontFamily: 'Proxima Nova', colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple), ), initialBinding: GlobalBinding(), home: AnimatedSplashWithNavigation(nextScreen: startScreen), ); } }