Commit f96fa9c8 authored by Sai Srinivas's avatar Sai Srinivas Committed by Sai Srinivas
Browse files

Firebase and Haptic

parent 7f695622
...@@ -6,6 +6,7 @@ plugins { ...@@ -6,6 +6,7 @@ plugins {
id("kotlin-android") id("kotlin-android")
// The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins. // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins.
id("dev.flutter.flutter-gradle-plugin") id("dev.flutter.flutter-gradle-plugin")
id("com.google.gms.google-services")
} }
val localProperties = Properties() val localProperties = Properties()
val localPropertiesFile = rootProject.file("local.properties") val localPropertiesFile = rootProject.file("local.properties")
...@@ -31,6 +32,8 @@ android { ...@@ -31,6 +32,8 @@ android {
compileOptions { compileOptions {
sourceCompatibility = JavaVersion.VERSION_11 sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11 targetCompatibility = JavaVersion.VERSION_11
isCoreLibraryDesugaringEnabled = true
} }
kotlinOptions { kotlinOptions {
...@@ -38,16 +41,15 @@ android { ...@@ -38,16 +41,15 @@ android {
} }
defaultConfig { defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId = "in.webgrid.pulse" applicationId = "in.webgrid.pulse"
// You can update the following values to match your application needs. // Firebase Messaging requires minSdk >= 23
// For more information, see: https://flutter.dev/to/review-gradle-config. minSdk = maxOf(flutter.minSdkVersion, 23)
minSdk = flutter.minSdkVersion
targetSdk = flutter.targetSdkVersion targetSdk = flutter.targetSdkVersion
versionCode = flutter.versionCode versionCode = flutter.versionCode
versionName = flutter.versionName versionName = flutter.versionName
} }
signingConfigs { signingConfigs {
create("release") { create("release") {
keyAlias = keystoreProperties["keyAlias"]?.toString() keyAlias = keystoreProperties["keyAlias"]?.toString()
...@@ -64,8 +66,15 @@ android { ...@@ -64,8 +66,15 @@ android {
signingConfig = signingConfigs.getByName("release") signingConfig = signingConfigs.getByName("release")
} }
} }
} }
flutter { flutter {
source = "../.." source = "../.."
} }
dependencies {
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.0.4")
}
{
"project_info": {
"project_number": "207322820833",
"project_id": "pulse-5cf9a",
"storage_bucket": "pulse-5cf9a.firebasestorage.app"
},
"client": [
{
"client_info": {
"mobilesdk_app_id": "1:207322820833:android:22a997a4a1d64fb04447a0",
"android_client_info": {
"package_name": "com.webgrid.pulse"
}
},
"oauth_client": [],
"api_key": [
{
"current_key": "AIzaSyCKS1W1HGZsOEbkTM0tmW-Qgo4VA4KoO2c"
}
],
"services": {
"appinvite_service": {
"other_platform_oauth_client": []
}
}
},
{
"client_info": {
"mobilesdk_app_id": "1:207322820833:android:82516ed462f6ae6e4447a0",
"android_client_info": {
"package_name": "in.webgrid.pulse"
}
},
"oauth_client": [],
"api_key": [
{
"current_key": "AIzaSyCKS1W1HGZsOEbkTM0tmW-Qgo4VA4KoO2c"
}
],
"services": {
"appinvite_service": {
"other_platform_oauth_client": []
}
}
}
],
"configuration_version": "1"
}
\ No newline at end of file
<manifest xmlns:android="http://schemas.android.com/apk/res/android"> <manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.VIBRATE" />
<application <application
android:label="Web Grid Pulse" android:label="Web Grid Pulse"
...@@ -34,6 +37,27 @@ ...@@ -34,6 +37,27 @@
<meta-data <meta-data
android:name="flutterEmbedding" android:name="flutterEmbedding"
android:value="2" /> android:value="2" />
<!-- Firebase Messaging service -->
<service
android:name="io.flutter.plugins.firebase.messaging.FlutterFirebaseMessagingService"
android:exported="false">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
<!-- Handle background messaging -->
<service
android:name="io.flutter.plugins.firebase.messaging.FlutterFirebaseMessagingBackgroundService"
android:exported="false" />
<!-- Needed for notifications to display correctly on Android 12+ -->
<meta-data
android:name="com.google.firebase.messaging.default_notification_channel_id"
android:value="default_channel_id" />
</application> </application>
<!-- Required to query activities that can process text, see: <!-- Required to query activities that can process text, see:
https://developer.android.com/training/package-visibility and https://developer.android.com/training/package-visibility and
......
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:pulse/Models/profileResponse.dart'; import 'package:pulse/Models/profileResponse.dart';
...@@ -38,11 +39,12 @@ class ProfileProvider extends ChangeNotifier { ...@@ -38,11 +39,12 @@ class ProfileProvider extends ChangeNotifier {
_errorMessage = null; _errorMessage = null;
_profileResponse = null; _profileResponse = null;
notifyListeners(); notifyListeners();
String? fcmToken = await FirebaseMessaging.instance.getToken();
final data = await ApiService.fetchProfile( final data = await ApiService.fetchProfile(
csrfToken: csrfToken, csrfToken: csrfToken,
sessionId: sessionId, sessionId: sessionId,
staffId: staffId, staffId: staffId,
token: fcmToken.toString() ?? "",
isApp: isApp, isApp: isApp,
); );
......
import 'package:flutter/material.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
class NotificationProvider with ChangeNotifier {
List<Map<String, dynamic>> _notifications = [];
int _unreadCount = 0;
List<Map<String, dynamic>> get notifications => _notifications;
int get unreadCount => _unreadCount;
// Add new notification
void addNotification(Map<String, dynamic> notification) {
_notifications.insert(0, notification);
_unreadCount++;
notifyListeners();
}
// Mark as read
void markAsRead(int index) {
if (index < _notifications.length) {
_notifications[index]['isRead'] = true;
_unreadCount--;
notifyListeners();
}
}
// Mark all as read
void markAllAsRead() {
for (var notification in _notifications) {
notification['isRead'] = true;
}
_unreadCount = 0;
notifyListeners();
}
// Clear all notifications
void clearAll() {
_notifications.clear();
_unreadCount = 0;
notifyListeners();
}
}
\ No newline at end of file
// import 'package:flutter/material.dart';
// import 'package:pulse/Screens/home_screen.dart';
// import 'package:webview_flutter/webview_flutter.dart';
//
//
// class WebErpScreen extends StatefulWidget {
// final String staffId;
// final String sessionId;
//
// const WebErpScreen({
// super.key,
// required this.staffId,
// required this.sessionId,
// });
//
// @override
// State<WebErpScreen> createState() => _WebErpScreenState();
// }
//
// class _WebErpScreenState extends State<WebErpScreen> {
// late final WebViewController _controller;
//
// @override
// void initState() {
// super.initState();
//
// _controller = WebViewController()
// ..setJavaScriptMode(JavaScriptMode.unrestricted)
// ..setNavigationDelegate(
// NavigationDelegate(
// onPageStarted: (url) {
// debugPrint("Loading: $url");
// },
// onPageFinished: (url) {
// debugPrint("Finished: $url");
// },
// ),
// )
// ..loadRequest(
// Uri.parse(
// "https://pulse.webgrid.in/app/authentication/web_erp?staff_id=${widget.staffId}&session_id=${widget.sessionId}",
// ),
// headers: {
// "Cookie": "session_id=${widget.sessionId}",
// },
// );
// }
//
// Future<bool> _handleWillPop() async {
// if (await _controller.canGoBack()) {
// _controller.goBack();
// return false; // don’t pop screen
// }
// return true; // pop screen
// }
//
// @override
// Widget build(BuildContext context) {
// return WillPopScope(
// onWillPop: _handleWillPop,
// child: Scaffold(
// appBar: AppBar(
// automaticallyImplyLeading: false,
// backgroundColor: AppColors.backgroundGradient2,
// title: Row(
// children: [
// InkResponse(
// onTap: () async {
// if (await _controller.canGoBack()) {
// _controller.goBack();
// } else {
// Navigator.pop(context, true);
// }
// },
// child: const Icon(
// Icons.arrow_back,
// color: Colors.white,
// size: 30,
// ),
// ),
// const SizedBox(width: 15),
// Text(
// "Web ERP",
// style: TextStyle(
// fontSize: 20,
// fontWeight: FontWeight.bold,
// color: AppColors.textPrimary,
// ),
// ),
// ],
// ),
// ),
// backgroundColor: const Color(0xFF1e293b),
// body: SafeArea(
// top: true,
// child: WebViewWidget(controller: _controller),
// ),
// ),
// );
// }
// }
...@@ -17,8 +17,14 @@ import 'package:flutter_inappwebview/flutter_inappwebview.dart'; ...@@ -17,8 +17,14 @@ import 'package:flutter_inappwebview/flutter_inappwebview.dart';
class HomeScreen extends StatefulWidget { class HomeScreen extends StatefulWidget {
final String sessionId; final String sessionId;
final String staffId; final String staffId;
final String? notificationUrl; //
const HomeScreen({super.key, required this.sessionId, required this.staffId}); const HomeScreen({
super.key,
required this.sessionId,
required this.staffId,
this.notificationUrl, //
});
@override @override
State<HomeScreen> createState() => _HomeScreenState(); State<HomeScreen> createState() => _HomeScreenState();
...@@ -49,6 +55,7 @@ class _HomeScreenState extends State<HomeScreen> { ...@@ -49,6 +55,7 @@ class _HomeScreenState extends State<HomeScreen> {
@override @override
void initState() { void initState() {
super.initState(); super.initState();
HapticFeedback.lightImpact();
_initConnectivity(); _initConnectivity();
_initializePullToRefresh(); _initializePullToRefresh();
...@@ -369,6 +376,7 @@ class _HomeScreenState extends State<HomeScreen> { ...@@ -369,6 +376,7 @@ class _HomeScreenState extends State<HomeScreen> {
builder: (context, profileProvider, child) { builder: (context, profileProvider, child) {
return InkWell( return InkWell(
onTap: () { onTap: () {
HapticFeedback.lightImpact();
Navigator.push( Navigator.push(
context, context,
MaterialPageRoute( MaterialPageRoute(
...@@ -476,13 +484,16 @@ class _HomeScreenState extends State<HomeScreen> { ...@@ -476,13 +484,16 @@ class _HomeScreenState extends State<HomeScreen> {
key: webViewKey, key: webViewKey,
initialUrlRequest: URLRequest( initialUrlRequest: URLRequest(
url: WebUri( url: WebUri(
"https://pulse.webgrid.in/app/authentication/web_erp?staff_id=${widget.staffId}&session_id=${widget.sessionId}", widget.notificationUrl != null && widget.notificationUrl!.isNotEmpty
? widget.notificationUrl!
: "https://pulse.webgrid.in/app/authentication/web_erp?staff_id=${widget.staffId}&session_id=${widget.sessionId}",
), ),
headers: {"Cookie": "session_id=${widget.sessionId}"}, headers: {"Cookie": "session_id=${widget.sessionId}"},
allowsCellularAccess: true, allowsCellularAccess: true,
allowsConstrainedNetworkAccess: true, allowsConstrainedNetworkAccess: true,
allowsExpensiveNetworkAccess: true, allowsExpensiveNetworkAccess: true,
), ),
initialOptions: InAppWebViewGroupOptions( initialOptions: InAppWebViewGroupOptions(
android: AndroidInAppWebViewOptions( android: AndroidInAppWebViewOptions(
useWideViewPort: true, useWideViewPort: true,
...@@ -530,12 +541,14 @@ class _HomeScreenState extends State<HomeScreen> { ...@@ -530,12 +541,14 @@ class _HomeScreenState extends State<HomeScreen> {
}, },
onLoadStart: (controller, url) { onLoadStart: (controller, url) {
setState(() { setState(() {
HapticFeedback.lightImpact();
_isLoading = true; _isLoading = true;
_isRefreshing = true; _isRefreshing = true;
}); });
}, },
onLoadStop: (controller, url) async { onLoadStop: (controller, url) async {
setState(() { setState(() {
HapticFeedback.lightImpact();
_isLoading = false; _isLoading = false;
_isRefreshing = false; _isRefreshing = false;
}); });
...@@ -545,6 +558,7 @@ class _HomeScreenState extends State<HomeScreen> { ...@@ -545,6 +558,7 @@ class _HomeScreenState extends State<HomeScreen> {
}, },
onLoadError: (controller, url, code, message) { onLoadError: (controller, url, code, message) {
setState(() { setState(() {
HapticFeedback.lightImpact();
_isLoading = false; _isLoading = false;
_isRefreshing = false; _isRefreshing = false;
}); });
...@@ -552,6 +566,7 @@ class _HomeScreenState extends State<HomeScreen> { ...@@ -552,6 +566,7 @@ class _HomeScreenState extends State<HomeScreen> {
}, },
onProgressChanged: (controller, progress) { onProgressChanged: (controller, progress) {
if (progress == 100) { if (progress == 100) {
HapticFeedback.lightImpact();
_pullToRefreshController?.endRefreshing(); _pullToRefreshController?.endRefreshing();
setState(() { setState(() {
_isRefreshing = false; _isRefreshing = false;
...@@ -559,6 +574,7 @@ class _HomeScreenState extends State<HomeScreen> { ...@@ -559,6 +574,7 @@ class _HomeScreenState extends State<HomeScreen> {
} }
}, },
onReceivedError: (controller, request, error) { onReceivedError: (controller, request, error) {
HapticFeedback.lightImpact();
setState(() { setState(() {
_isLoading = false; _isLoading = false;
_isRefreshing = false; _isRefreshing = false;
......
import 'package:cached_network_image/cached_network_image.dart'; import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:pulse/Screens/authScreen/LoginScreen.dart'; import 'package:pulse/Screens/authScreen/LoginScreen.dart';
import 'package:pulse/Screens/home_screen.dart'; import 'package:pulse/Screens/home_screen.dart';
...@@ -86,7 +87,10 @@ class _ProfileScreenState extends State<ProfileScreen> { ...@@ -86,7 +87,10 @@ class _ProfileScreenState extends State<ProfileScreen> {
), ),
child: IconButton( child: IconButton(
icon: const Icon(Icons.arrow_back, color: Colors.white), icon: const Icon(Icons.arrow_back, color: Colors.white),
onPressed: () => Navigator.pop(context), onPressed: () {
HapticFeedback.lightImpact();
Navigator.pop(context, true);
},
), ),
), ),
const SizedBox(width: 16), const SizedBox(width: 16),
...@@ -356,6 +360,7 @@ class _ProfileScreenState extends State<ProfileScreen> { ...@@ -356,6 +360,7 @@ class _ProfileScreenState extends State<ProfileScreen> {
), ),
child: ElevatedButton( child: ElevatedButton(
onPressed: () { onPressed: () {
HapticFeedback.lightImpact();
_showLogoutConfirmationDialog(); _showLogoutConfirmationDialog();
}, },
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
...@@ -454,7 +459,10 @@ class _ProfileScreenState extends State<ProfileScreen> { ...@@ -454,7 +459,10 @@ class _ProfileScreenState extends State<ProfileScreen> {
children: [ children: [
Expanded( Expanded(
child: OutlinedButton( child: OutlinedButton(
onPressed: () => Navigator.pop(context), onPressed: () {
HapticFeedback.lightImpact();
Navigator.pop(context);
},
style: OutlinedButton.styleFrom( style: OutlinedButton.styleFrom(
foregroundColor: Colors.grey.shade700, foregroundColor: Colors.grey.shade700,
side: BorderSide(color: Colors.grey.shade400), side: BorderSide(color: Colors.grey.shade400),
...@@ -470,6 +478,7 @@ class _ProfileScreenState extends State<ProfileScreen> { ...@@ -470,6 +478,7 @@ class _ProfileScreenState extends State<ProfileScreen> {
Expanded( Expanded(
child: ElevatedButton( child: ElevatedButton(
onPressed: () async { onPressed: () async {
HapticFeedback.lightImpact();
Navigator.pop(context); // Close dialog first Navigator.pop(context); // Close dialog first
await _performLogout(); await _performLogout();
}, },
......
...@@ -100,13 +100,16 @@ class ApiService { ...@@ -100,13 +100,16 @@ class ApiService {
required String csrfToken, required String csrfToken,
required String sessionId, required String sessionId,
required String staffId, required String staffId,
required token,
String isApp = "1", String isApp = "1",
}) async { }) async {
try { try {
debugPrint("⚠️ FCM Device token: $token");
Map<String, String> data = { Map<String, String> data = {
"csrf_token_name": csrfToken, "csrf_token_name": csrfToken,
"session_id": sessionId, "session_id": sessionId,
"staff_id": staffId, "staff_id": staffId,
'token_id': (token).toString(),
"is_app": isApp, "is_app": isApp,
}; };
......
...@@ -2,6 +2,8 @@ import 'dart:async'; ...@@ -2,6 +2,8 @@ import 'dart:async';
import 'dart:io'; import 'dart:io';
import 'package:connectivity_plus/connectivity_plus.dart'; import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:package_info_plus/package_info_plus.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:pulse/Notifier/auth_provider.dart'; import 'package:pulse/Notifier/auth_provider.dart';
import 'package:pulse/Screens/authScreen/LoginScreen.dart'; import 'package:pulse/Screens/authScreen/LoginScreen.dart';
...@@ -19,6 +21,7 @@ class SplashScreen extends StatefulWidget { ...@@ -19,6 +21,7 @@ class SplashScreen extends StatefulWidget {
class _SplashScreenState extends State<SplashScreen> with SingleTickerProviderStateMixin { class _SplashScreenState extends State<SplashScreen> with SingleTickerProviderStateMixin {
final _prefs = SharedPreferencesService.instance; final _prefs = SharedPreferencesService.instance;
String _appVersion = "";
double _opacity = 0.0; double _opacity = 0.0;
double _scale = 0.8; double _scale = 0.8;
double _progressValue = 0.0; double _progressValue = 0.0;
...@@ -31,7 +34,7 @@ class _SplashScreenState extends State<SplashScreen> with SingleTickerProviderSt ...@@ -31,7 +34,7 @@ class _SplashScreenState extends State<SplashScreen> with SingleTickerProviderSt
@override @override
void initState() { void initState() {
super.initState(); super.initState();
_loadAppVersion();
// Initialize connectivity check // Initialize connectivity check
_initConnectivity(); _initConnectivity();
...@@ -57,6 +60,13 @@ class _SplashScreenState extends State<SplashScreen> with SingleTickerProviderSt ...@@ -57,6 +60,13 @@ class _SplashScreenState extends State<SplashScreen> with SingleTickerProviderSt
_simulateProgress(); _simulateProgress();
} }
Future<void> _loadAppVersion() async {
final info = await PackageInfo.fromPlatform();
setState(() {
_appVersion = "${info.version}+${info.buildNumber}";
});
}
Future<void> _initConnectivity() async { Future<void> _initConnectivity() async {
try { try {
// Initial connectivity check // Initial connectivity check
...@@ -122,6 +132,7 @@ class _SplashScreenState extends State<SplashScreen> with SingleTickerProviderSt ...@@ -122,6 +132,7 @@ class _SplashScreenState extends State<SplashScreen> with SingleTickerProviderSt
// Hide any existing snackbar // Hide any existing snackbar
ScaffoldMessenger.of(context).hideCurrentSnackBar(); ScaffoldMessenger.of(context).hideCurrentSnackBar();
// Show custom snackbar with all required params // Show custom snackbar with all required params
CustomSnackBar.show( CustomSnackBar.show(
context: context, context: context,
...@@ -141,6 +152,7 @@ class _SplashScreenState extends State<SplashScreen> with SingleTickerProviderSt ...@@ -141,6 +152,7 @@ class _SplashScreenState extends State<SplashScreen> with SingleTickerProviderSt
void _simulateProgress() { void _simulateProgress() {
Timer.periodic(const Duration(milliseconds: 30), (timer) { Timer.periodic(const Duration(milliseconds: 30), (timer) {
if (_progressValue < 1.0) { if (_progressValue < 1.0) {
//HapticFeedback.lightImpact();
setState(() { setState(() {
_progressValue += 0.02; _progressValue += 0.02;
}); });
...@@ -195,6 +207,7 @@ class _SplashScreenState extends State<SplashScreen> with SingleTickerProviderSt ...@@ -195,6 +207,7 @@ class _SplashScreenState extends State<SplashScreen> with SingleTickerProviderSt
); );
if (loginProvider.isLoggedIn) { if (loginProvider.isLoggedIn) {
HapticFeedback.lightImpact();
Navigator.pushReplacement( Navigator.pushReplacement(
context, context,
MaterialPageRoute( MaterialPageRoute(
...@@ -209,13 +222,13 @@ class _SplashScreenState extends State<SplashScreen> with SingleTickerProviderSt ...@@ -209,13 +222,13 @@ class _SplashScreenState extends State<SplashScreen> with SingleTickerProviderSt
} }
// Default → Login screen // Default → Login screen
HapticFeedback.lightImpact();
Navigator.pushReplacement( Navigator.pushReplacement(
context, context,
MaterialPageRoute(builder: (_) => const LoginScreen()), MaterialPageRoute(builder: (_) => const LoginScreen()),
); );
} }
@override @override
void dispose() { void dispose() {
_controller.dispose(); _controller.dispose();
...@@ -418,7 +431,7 @@ class _SplashScreenState extends State<SplashScreen> with SingleTickerProviderSt ...@@ -418,7 +431,7 @@ class _SplashScreenState extends State<SplashScreen> with SingleTickerProviderSt
// Version info // Version info
Text( Text(
"Version 1.0.0", "Version $_appVersion",
style: TextStyle( style: TextStyle(
color: Colors.white.withOpacity(0.5), color: Colors.white.withOpacity(0.5),
fontSize: 12, fontSize: 12,
......
import 'dart:io';
import 'dart:convert';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart'; import 'package:flutter/services.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:pulse/SplashScreen.dart';
import 'package:pulse/Notifier/auth_provider.dart';
import 'package:pulse/Notifier/theme_provider.dart';
import 'package:pulse/Notifier/ProfileProvider.dart'; import 'package:pulse/Notifier/ProfileProvider.dart';
import 'package:pulse/Notifier/webProvider.dart'; import 'package:pulse/Notifier/webProvider.dart';
import 'package:pulse/SplashScreen.dart'; import 'package:flutter_inappwebview/flutter_inappwebview.dart';
import 'Notifier/auth_provider.dart'; import 'Screens/home_screen.dart';
import 'Notifier/theme_provider.dart';
import 'Screens/authScreen/LoginScreen.dart';
// Needed to navigate outside BuildContext
final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
// Initialize the notification plugin
final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
FlutterLocalNotificationsPlugin();
// Handle background notifications
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
if (Firebase.apps.isEmpty) {
await Firebase.initializeApp();
}
print("📬 Background message: ${message.messageId}");
}
void main() { Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized(); WidgetsFlutterBinding.ensureInitialized();
// Initialize Firebase safely
try {
if (Firebase.apps.isEmpty) {
await Firebase.initializeApp(
options: const FirebaseOptions(
apiKey: "2f663c764bb77355c82d30b2068b15427363ccfd",
appId: "1:207322820833:android:82516ed462f6ae6e4447a0",
messagingSenderId: "207322820833",
projectId: "pulse-5cf9a",
),
);
debugPrint(" Firebase initialized.");
} else {
Firebase.app(); // existing instance
debugPrint("⚡ Firebase already initialized, reusing instance.");
}
} catch (e) {
debugPrint("❌ Firebase init error: $e");
}
// Setup local notifications
const AndroidInitializationSettings initializationSettingsAndroid =
AndroidInitializationSettings('@mipmap/ic_launcher');
const InitializationSettings initializationSettings =
InitializationSettings(android: initializationSettingsAndroid);
await flutterLocalNotificationsPlugin.initialize(initializationSettings);
// Local notifications setup
const AndroidInitializationSettings initSettingsAndroid =
AndroidInitializationSettings('@mipmap/ic_launcher');
const InitializationSettings initSettings =
InitializationSettings(android: initSettingsAndroid);
await flutterLocalNotificationsPlugin.initialize(
initSettings,
onDidReceiveNotificationResponse: (NotificationResponse response) {
if (response.payload != null) {
final data = jsonDecode(response.payload!);
NotificationHandler.handle(data);
}
},
);
// Setup FCM
FirebaseMessaging messaging = FirebaseMessaging.instance;
await messaging.requestPermission(alert: true, badge: true, sound: true);
// String? token = await messaging.getToken();
// print("🔥 FCM Token: $token");
// Background handler
FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
// Foreground notification
FirebaseMessaging.onMessage.listen((RemoteMessage message) async {
print("💬 Foreground message: ${message.notification?.title}");
RemoteNotification? notification = message.notification;
AndroidNotification? android = message.notification?.android;
if (notification != null && android != null) {
flutterLocalNotificationsPlugin.show(
notification.hashCode,
notification.title,
notification.body,
const NotificationDetails(
android: AndroidNotificationDetails(
'pulse_channel', // channel id
'Pulse Notifications', // channel name
importance: Importance.max,
priority: Priority.high,
),
),
payload: jsonEncode(message.data),
);
}
});
// notification while app is in background
FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) {
print(" Notification clicked: ${message.data}");
});
InAppWebViewController.setWebContentsDebuggingEnabled(true); InAppWebViewController.setWebContentsDebuggingEnabled(true);
runApp(const MyApp()); runApp(const MyApp());
} }
...@@ -23,10 +127,10 @@ class MyApp extends StatelessWidget { ...@@ -23,10 +127,10 @@ class MyApp extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return MultiProvider( return MultiProvider(
providers: [ providers: [
ChangeNotifierProvider<AuthProvider>(create: (_) => AuthProvider(),), ChangeNotifierProvider<AuthProvider>(create: (_) => AuthProvider()),
ChangeNotifierProvider<ThemeProvider>(create: (_) => ThemeProvider(),), ChangeNotifierProvider<ThemeProvider>(create: (_) => ThemeProvider()),
ChangeNotifierProvider<ProfileProvider>(create: (_) => ProfileProvider(),), ChangeNotifierProvider<ProfileProvider>(create: (_) => ProfileProvider()),
ChangeNotifierProvider<WebErpProvider>(create: (_) => WebErpProvider(),), ChangeNotifierProvider<WebErpProvider>(create: (_) => WebErpProvider()),
], ],
child: Consumer<ThemeProvider>( child: Consumer<ThemeProvider>(
builder: (context, themeProvider, child) { builder: (context, themeProvider, child) {
...@@ -45,3 +149,29 @@ class MyApp extends StatelessWidget { ...@@ -45,3 +149,29 @@ class MyApp extends StatelessWidget {
); );
} }
} }
class NotificationHandler {
static void handle(Map<String, dynamic> data) {
final type = data['type'];
final value = data['type_value'];
final notifId = data['notification_id'];
debugPrint("🔗 Handling Notification:");
debugPrint("type=$type, value=$value, id=$notifId");
// Decide what to do based on the type
if (type == 'web' && value != null) {
navigatorKey.currentState?.push(
MaterialPageRoute(
builder: (_) => HomeScreen(
sessionId: '',
staffId: '',
notificationUrl: value,
),
),
);
}
}
}
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
class CustomSnackBar { class CustomSnackBar {
static void show({ static void show({
...@@ -9,6 +10,7 @@ class CustomSnackBar { ...@@ -9,6 +10,7 @@ class CustomSnackBar {
Color backgroundColor = const Color(0xFF324563), Color backgroundColor = const Color(0xFF324563),
Duration duration = const Duration(seconds: 4), Duration duration = const Duration(seconds: 4),
}) { }) {
HapticFeedback.lightImpact();
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
SnackBar( SnackBar(
backgroundColor: Colors.transparent, backgroundColor: Colors.transparent,
......
...@@ -6,7 +6,10 @@ import FlutterMacOS ...@@ -6,7 +6,10 @@ import FlutterMacOS
import Foundation import Foundation
import connectivity_plus import connectivity_plus
import firebase_core
import firebase_messaging
import flutter_inappwebview_macos import flutter_inappwebview_macos
import flutter_local_notifications
import package_info_plus import package_info_plus
import path_provider_foundation import path_provider_foundation
import shared_preferences_foundation import shared_preferences_foundation
...@@ -15,7 +18,10 @@ import webview_flutter_wkwebview ...@@ -15,7 +18,10 @@ import webview_flutter_wkwebview
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
ConnectivityPlusPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlusPlugin")) ConnectivityPlusPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlusPlugin"))
FLTFirebaseCorePlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseCorePlugin"))
FLTFirebaseMessagingPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseMessagingPlugin"))
InAppWebViewFlutterPlugin.register(with: registry.registrar(forPlugin: "InAppWebViewFlutterPlugin")) InAppWebViewFlutterPlugin.register(with: registry.registrar(forPlugin: "InAppWebViewFlutterPlugin"))
FlutterLocalNotificationsPlugin.register(with: registry.registrar(forPlugin: "FlutterLocalNotificationsPlugin"))
FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin")) FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin"))
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
......
# Generated by pub # Generated by pub
# See https://dart.dev/tools/pub/glossary#lockfile # See https://dart.dev/tools/pub/glossary#lockfile
packages: packages:
_flutterfire_internals:
dependency: transitive
description:
name: _flutterfire_internals
sha256: "23d16f00a2da8ffa997c782453c73867b0609bd90435195671a54de38a3566df"
url: "https://pub.dev"
source: hosted
version: "1.3.62"
args: args:
dependency: transitive dependency: transitive
description: description:
...@@ -153,6 +161,54 @@ packages: ...@@ -153,6 +161,54 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "7.0.1" version: "7.0.1"
firebase_core:
dependency: "direct main"
description:
name: firebase_core
sha256: "4dd96f05015c0dcceaa47711394c32971aee70169625d5e2477e7676c01ce0ee"
url: "https://pub.dev"
source: hosted
version: "4.1.1"
firebase_core_platform_interface:
dependency: transitive
description:
name: firebase_core_platform_interface
sha256: "5873a370f0d232918e23a5a6137dbe4c2c47cf017301f4ea02d9d636e52f60f0"
url: "https://pub.dev"
source: hosted
version: "6.0.1"
firebase_core_web:
dependency: transitive
description:
name: firebase_core_web
sha256: "61a51037312dac781f713308903bb7a1762a7f92f7bc286a3a0947fb2a713b82"
url: "https://pub.dev"
source: hosted
version: "3.1.1"
firebase_messaging:
dependency: "direct main"
description:
name: firebase_messaging
sha256: ba12ad0b600e0c939fbb9391e1cd3320a5b5dad5284276b9182fc21eb1e72c2b
url: "https://pub.dev"
source: hosted
version: "16.0.2"
firebase_messaging_platform_interface:
dependency: transitive
description:
name: firebase_messaging_platform_interface
sha256: b4bade67bfc09fcc56eb012b3fc72b59ca9e2259a34cdfb81b368169770ff536
url: "https://pub.dev"
source: hosted
version: "4.7.2"
firebase_messaging_web:
dependency: transitive
description:
name: firebase_messaging_web
sha256: "8ae4a00d178993feb79603cad324b53696375cbb78805e8eb603fe331866629d"
url: "https://pub.dev"
source: hosted
version: "4.0.2"
fixnum: fixnum:
dependency: transitive dependency: transitive
description: description:
...@@ -254,6 +310,30 @@ packages: ...@@ -254,6 +310,30 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "5.0.0" version: "5.0.0"
flutter_local_notifications:
dependency: "direct main"
description:
name: flutter_local_notifications
sha256: "674173fd3c9eda9d4c8528da2ce0ea69f161577495a9cc835a2a4ecd7eadeb35"
url: "https://pub.dev"
source: hosted
version: "17.2.4"
flutter_local_notifications_linux:
dependency: transitive
description:
name: flutter_local_notifications_linux
sha256: c49bd06165cad9beeb79090b18cd1eb0296f4bf4b23b84426e37dd7c027fc3af
url: "https://pub.dev"
source: hosted
version: "4.0.1"
flutter_local_notifications_platform_interface:
dependency: transitive
description:
name: flutter_local_notifications_platform_interface
sha256: "85f8d07fe708c1bdcf45037f2c0109753b26ae077e9d9e899d55971711a4ea66"
url: "https://pub.dev"
source: hosted
version: "7.2.0"
flutter_svg: flutter_svg:
dependency: "direct main" dependency: "direct main"
description: description:
...@@ -725,6 +805,14 @@ packages: ...@@ -725,6 +805,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.7.4" version: "0.7.4"
timezone:
dependency: transitive
description:
name: timezone
sha256: "2236ec079a174ce07434e89fcd3fcda430025eb7692244139a9cf54fdcf1fc7d"
url: "https://pub.dev"
source: hosted
version: "0.9.4"
typed_data: typed_data:
dependency: transitive dependency: transitive
description: description:
......
...@@ -45,7 +45,10 @@ dependencies: ...@@ -45,7 +45,10 @@ dependencies:
flutter_html: ^3.0.0 flutter_html: ^3.0.0
connectivity_plus: ^6.1.0 connectivity_plus: ^6.1.0
pull_to_refresh: ^2.0.0 pull_to_refresh: ^2.0.0
firebase_messaging: ^16.0.2
flutter_inappwebview: ^6.1.5 flutter_inappwebview: ^6.1.5
firebase_core: ^4.1.1
flutter_local_notifications: ^17.2.0
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:
......
...@@ -7,12 +7,15 @@ ...@@ -7,12 +7,15 @@
#include "generated_plugin_registrant.h" #include "generated_plugin_registrant.h"
#include <connectivity_plus/connectivity_plus_windows_plugin.h> #include <connectivity_plus/connectivity_plus_windows_plugin.h>
#include <firebase_core/firebase_core_plugin_c_api.h>
#include <flutter_inappwebview_windows/flutter_inappwebview_windows_plugin_c_api.h> #include <flutter_inappwebview_windows/flutter_inappwebview_windows_plugin_c_api.h>
#include <permission_handler_windows/permission_handler_windows_plugin.h> #include <permission_handler_windows/permission_handler_windows_plugin.h>
void RegisterPlugins(flutter::PluginRegistry* registry) { void RegisterPlugins(flutter::PluginRegistry* registry) {
ConnectivityPlusWindowsPluginRegisterWithRegistrar( ConnectivityPlusWindowsPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("ConnectivityPlusWindowsPlugin")); registry->GetRegistrarForPlugin("ConnectivityPlusWindowsPlugin"));
FirebaseCorePluginCApiRegisterWithRegistrar(
registry->GetRegistrarForPlugin("FirebaseCorePluginCApi"));
FlutterInappwebviewWindowsPluginCApiRegisterWithRegistrar( FlutterInappwebviewWindowsPluginCApiRegisterWithRegistrar(
registry->GetRegistrarForPlugin("FlutterInappwebviewWindowsPluginCApi")); registry->GetRegistrarForPlugin("FlutterInappwebviewWindowsPluginCApi"));
PermissionHandlerWindowsPluginRegisterWithRegistrar( PermissionHandlerWindowsPluginRegisterWithRegistrar(
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
list(APPEND FLUTTER_PLUGIN_LIST list(APPEND FLUTTER_PLUGIN_LIST
connectivity_plus connectivity_plus
firebase_core
flutter_inappwebview_windows flutter_inappwebview_windows
permission_handler_windows permission_handler_windows
) )
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment