Files
2026-05-27 11:45:53 +05:30

410 lines
13 KiB
Dart

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<void> _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<AndroidFlutterLocalNotificationsPlugin>();
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<void> _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<AndroidFlutterLocalNotificationsPlugin>()
?.createNotificationChannel(channel);
}
}
// -------------------------
// ROUTE OBSERVER
// -------------------------
final RouteObserver<ModalRoute<void>> routeObserver =
RouteObserver<ModalRoute<void>>();
// -------------------------
// APP VERSION CHECK
// -------------------------
Future<void> _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<bool> _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<AnimatedSplashWithNavigation> createState() =>
_AnimatedSplashWithNavigationState();
}
class _AnimatedSplashWithNavigationState
extends State<AnimatedSplashWithNavigation> {
@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<void> 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),
);
}
}