From 3e0e46d2b930e555426fde9e50314a262406c74f Mon Sep 17 00:00:00 2001 From: kratikpal Date: Fri, 9 Aug 2024 10:32:43 +0530 Subject: [PATCH] push notification --- .vscode/settings.json | 3 + android/app/build.gradle | 3 + android/app/google-services.json | 29 +++ android/settings.gradle | 3 + firebase.json | 1 + lib/firebase_options.dart | 69 ++++++ lib/main.dart | 97 +++++++- lib/models/notification_list_response.dart | 67 +++++ lib/notification_service.dart | 165 +++++++++++++ lib/provider/notification_provider.dart | 40 +++ lib/screens/home_screen.dart | 9 + lib/screens/notification_screen.dart | 231 +++++++++--------- lib/screens/rejected_application_screen.dart | 111 ++++----- lib/services/api_urls.dart | 1 + macos/Flutter/GeneratedPluginRegistrant.swift | 6 + pubspec.lock | 96 ++++++++ pubspec.yaml | 3 + .../flutter/generated_plugin_registrant.cc | 3 + windows/flutter/generated_plugins.cmake | 1 + 19 files changed, 747 insertions(+), 191 deletions(-) create mode 100644 .vscode/settings.json create mode 100644 android/app/google-services.json create mode 100644 firebase.json create mode 100644 lib/firebase_options.dart create mode 100644 lib/models/notification_list_response.dart create mode 100644 lib/notification_service.dart create mode 100644 lib/provider/notification_provider.dart diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..e0f15db --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "java.configuration.updateBuildConfiguration": "automatic" +} \ No newline at end of file diff --git a/android/app/build.gradle b/android/app/build.gradle index e2ee37a..85f2127 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -1,5 +1,8 @@ plugins { id "com.android.application" + // START: FlutterFire Configuration + id 'com.google.gms.google-services' + // END: FlutterFire Configuration id "kotlin-android" // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins. id "dev.flutter.flutter-gradle-plugin" diff --git a/android/app/google-services.json b/android/app/google-services.json new file mode 100644 index 0000000..40ed628 --- /dev/null +++ b/android/app/google-services.json @@ -0,0 +1,29 @@ +{ + "project_info": { + "project_number": "242950171023", + "project_id": "cheminova-1fcf0", + "storage_bucket": "cheminova-1fcf0.appspot.com" + }, + "client": [ + { + "client_info": { + "mobilesdk_app_id": "1:242950171023:android:7fdc614b0429b52445c3fa", + "android_client_info": { + "package_name": "com.example.cheminova" + } + }, + "oauth_client": [], + "api_key": [ + { + "current_key": "AIzaSyDfgOZAwDgUnzQYnIHm8ObxrDtTutmAoAE" + } + ], + "services": { + "appinvite_service": { + "other_platform_oauth_client": [] + } + } + } + ], + "configuration_version": "1" +} \ No newline at end of file diff --git a/android/settings.gradle b/android/settings.gradle index c5fb685..f7d6575 100644 --- a/android/settings.gradle +++ b/android/settings.gradle @@ -19,6 +19,9 @@ pluginManagement { plugins { id "dev.flutter.flutter-plugin-loader" version "1.0.0" id "com.android.application" version "7.3.0" apply false + // START: FlutterFire Configuration + id "com.google.gms.google-services" version "4.3.15" apply false + // END: FlutterFire Configuration id "org.jetbrains.kotlin.android" version "1.9.10" apply false } diff --git a/firebase.json b/firebase.json new file mode 100644 index 0000000..1e338e8 --- /dev/null +++ b/firebase.json @@ -0,0 +1 @@ +{"flutter":{"platforms":{"android":{"default":{"projectId":"cheminova-1fcf0","appId":"1:242950171023:android:7fdc614b0429b52445c3fa","fileOutput":"android/app/google-services.json"}},"dart":{"lib/firebase_options.dart":{"projectId":"cheminova-1fcf0","configurations":{"android":"1:242950171023:android:7fdc614b0429b52445c3fa","ios":"1:242950171023:ios:f4aba8c6c2d8f5ec45c3fa"}}}}}} \ No newline at end of file diff --git a/lib/firebase_options.dart b/lib/firebase_options.dart new file mode 100644 index 0000000..a23bc43 --- /dev/null +++ b/lib/firebase_options.dart @@ -0,0 +1,69 @@ +// File generated by FlutterFire CLI. +// ignore_for_file: type=lint +import 'package:firebase_core/firebase_core.dart' show FirebaseOptions; +import 'package:flutter/foundation.dart' + show defaultTargetPlatform, kIsWeb, TargetPlatform; + +/// Default [FirebaseOptions] for use with your Firebase apps. +/// +/// Example: +/// ```dart +/// import 'firebase_options.dart'; +/// // ... +/// await Firebase.initializeApp( +/// options: DefaultFirebaseOptions.currentPlatform, +/// ); +/// ``` +class DefaultFirebaseOptions { + static FirebaseOptions get currentPlatform { + if (kIsWeb) { + throw UnsupportedError( + 'DefaultFirebaseOptions have not been configured for web - ' + 'you can reconfigure this by running the FlutterFire CLI again.', + ); + } + switch (defaultTargetPlatform) { + case TargetPlatform.android: + return android; + case TargetPlatform.iOS: + return ios; + case TargetPlatform.macOS: + throw UnsupportedError( + 'DefaultFirebaseOptions have not been configured for macos - ' + 'you can reconfigure this by running the FlutterFire CLI again.', + ); + case TargetPlatform.windows: + throw UnsupportedError( + 'DefaultFirebaseOptions have not been configured for windows - ' + 'you can reconfigure this by running the FlutterFire CLI again.', + ); + case TargetPlatform.linux: + throw UnsupportedError( + 'DefaultFirebaseOptions have not been configured for linux - ' + 'you can reconfigure this by running the FlutterFire CLI again.', + ); + default: + throw UnsupportedError( + 'DefaultFirebaseOptions are not supported for this platform.', + ); + } + } + + static const FirebaseOptions android = FirebaseOptions( + apiKey: 'AIzaSyDfgOZAwDgUnzQYnIHm8ObxrDtTutmAoAE', + appId: '1:242950171023:android:7fdc614b0429b52445c3fa', + messagingSenderId: '242950171023', + projectId: 'cheminova-1fcf0', + storageBucket: 'cheminova-1fcf0.appspot.com', + ); + + static const FirebaseOptions ios = FirebaseOptions( + apiKey: 'AIzaSyCukVtk1vpdHcO1ayiPMz1wLQGfRGzEXxk', + appId: '1:242950171023:ios:f4aba8c6c2d8f5ec45c3fa', + messagingSenderId: '242950171023', + projectId: 'cheminova-1fcf0', + storageBucket: 'cheminova-1fcf0.appspot.com', + iosBundleId: 'com.example.cheminova', + ); + +} \ No newline at end of file diff --git a/lib/main.dart b/lib/main.dart index 8eeccca..5b6c353 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,19 +1,104 @@ +import 'dart:developer'; +import 'dart:io'; import 'package:cheminova/provider/collect_kyc_provider.dart'; import 'package:cheminova/provider/user_provider.dart'; import 'package:cheminova/screens/splash_screen.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_local_notifications/flutter_local_notifications.dart'; import 'package:provider/provider.dart'; +import 'firebase_options.dart'; -void main() { - runApp(MultiProvider(providers: [ - ChangeNotifierProvider(create: (context) => CollectKycProvider()), - ChangeNotifierProvider(create: (_) => UserProvider()), - ], child: const MyApp())); +@pragma('vm:entry-point') +Future _firebaseMessagingBackgroundHandler(RemoteMessage message) async { + await Firebase.initializeApp(); + log("Handling a background message: ${message.data["data"]}"); } -class MyApp extends StatelessWidget { +AndroidNotificationChannel channel = const AndroidNotificationChannel( + 'High Importance Channel', 'High Importance Notifications', + importance: Importance.high); + +late FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin; + +Future main() async { + WidgetsFlutterBinding.ensureInitialized(); + + await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform); + + if (!kIsWeb) { + flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin(); + + FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler); + + await FirebaseMessaging.instance.getInitialMessage().then((message) {}); + + FirebaseMessaging.onMessage.listen((RemoteMessage message) { + RemoteNotification? notification = message.notification; + AndroidNotification? android = message.notification?.android; + if (notification != null && android != null) { + flutterLocalNotificationsPlugin.show( + notification.hashCode, + notification.title, + notification.body, + NotificationDetails( + android: AndroidNotificationDetails(channel.id, channel.name, + icon: '@mipmap/ic_launcher', importance: Importance.max), + iOS: const DarwinNotificationDetails())); + const AndroidInitializationSettings initializationSettingsAndroid = + AndroidInitializationSettings('@mipmap/ic_launcher'); + const DarwinInitializationSettings darwinInitializationSettings = + DarwinInitializationSettings(); + var initSettings = const InitializationSettings( + android: initializationSettingsAndroid, + iOS: darwinInitializationSettings); + flutterLocalNotificationsPlugin.initialize(initSettings); + } + }); + + if (Platform.isAndroid) { + await flutterLocalNotificationsPlugin + .resolvePlatformSpecificImplementation< + AndroidFlutterLocalNotificationsPlugin>() + ?.requestNotificationsPermission(); + await flutterLocalNotificationsPlugin + .resolvePlatformSpecificImplementation< + AndroidFlutterLocalNotificationsPlugin>() + ?.createNotificationChannel(channel); + } + if (Platform.isIOS) { + await flutterLocalNotificationsPlugin + .resolvePlatformSpecificImplementation< + IOSFlutterLocalNotificationsPlugin>() + ?.requestPermissions(alert: true, badge: true, sound: true); + } + + await FirebaseMessaging.instance + .setForegroundNotificationPresentationOptions( + alert: true, badge: true, sound: true); + } + + runApp( + MultiProvider( + providers: [ + ChangeNotifierProvider(create: (context) => CollectKycProvider()), + ChangeNotifierProvider(create: (_) => UserProvider()), + ], + child: const MyApp(), + ), + ); +} + +class MyApp extends StatefulWidget { const MyApp({super.key}); + @override + State createState() => _MyAppState(); +} + +class _MyAppState extends State { @override Widget build(BuildContext context) { return MaterialApp( diff --git a/lib/models/notification_list_response.dart b/lib/models/notification_list_response.dart new file mode 100644 index 0000000..4db128d --- /dev/null +++ b/lib/models/notification_list_response.dart @@ -0,0 +1,67 @@ +class NotificationListResponse { + String? returnMessage; + List? notifications; + + NotificationListResponse({this.returnMessage, this.notifications}); + + NotificationListResponse.fromJson(Map json) { + returnMessage = json['return_message']; + if (json['notifications'] != null) { + notifications = []; + json['notifications'].forEach((v) { + notifications!.add(new Notifications.fromJson(v)); + }); + } + } + + Map toJson() { + final Map data = new Map(); + data['return_message'] = this.returnMessage; + if (this.notifications != null) { + data['notifications'] = + this.notifications!.map((v) => v.toJson()).toList(); + } + return data; + } +} + +class Notifications { + String? sId; + String? title; + String? msg; + String? addedFor; + String? createdAt; + String? updatedAt; + int? iV; + + Notifications( + {this.sId, + this.title, + this.msg, + this.addedFor, + this.createdAt, + this.updatedAt, + this.iV}); + + Notifications.fromJson(Map json) { + sId = json['_id']; + title = json['title']; + msg = json['msg']; + addedFor = json['added_for']; + createdAt = json['createdAt']; + updatedAt = json['updatedAt']; + iV = json['__v']; + } + + Map toJson() { + final Map data = new Map(); + data['_id'] = this.sId; + data['title'] = this.title; + data['msg'] = this.msg; + data['added_for'] = this.addedFor; + data['createdAt'] = this.createdAt; + data['updatedAt'] = this.updatedAt; + data['__v'] = this.iV; + return data; + } +} diff --git a/lib/notification_service.dart b/lib/notification_service.dart new file mode 100644 index 0000000..6777ade --- /dev/null +++ b/lib/notification_service.dart @@ -0,0 +1,165 @@ +import 'dart:io'; + +import 'package:firebase_messaging/firebase_messaging.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_local_notifications/flutter_local_notifications.dart'; + +class NotificationServices { + //initialising firebase message plugin + FirebaseMessaging messaging = FirebaseMessaging.instance; + + //initialising firebase message plugin + final FlutterLocalNotificationsPlugin _flutterLocalNotificationsPlugin = + FlutterLocalNotificationsPlugin(); + + //function to initialise flutter local notification plugin to show notifications for android when app is active + void initLocalNotifications( + BuildContext context, RemoteMessage message) async { + var androidInitializationSettings = + const AndroidInitializationSettings('@mipmap/ic_launcher'); + var iosInitializationSettings = const DarwinInitializationSettings(); + + var initializationSetting = InitializationSettings( + android: androidInitializationSettings, iOS: iosInitializationSettings); + + await _flutterLocalNotificationsPlugin.initialize(initializationSetting, + onDidReceiveNotificationResponse: (payload) { + // handle interaction when app is active for android + handleMessage(context, message); + }); + } + + void firebaseInit(BuildContext context) { + FirebaseMessaging.onMessage.listen((message) { + RemoteNotification? notification = message.notification; + AndroidNotification? android = message.notification!.android; + + if (kDebugMode) { + print("notifications title:${notification!.title}"); + print("notifications body:${notification.body}"); + print('count:${android!.count}'); + print('data:${message.data.toString()}'); + } + + if (Platform.isIOS) { + forgroundMessage(); + } + + if (Platform.isAndroid) { + initLocalNotifications(context, message); + showNotification(message); + } + }); + } + + void requestNotificationPermission() async { + NotificationSettings settings = await messaging.requestPermission( + alert: true, + announcement: true, + badge: true, + carPlay: true, + criticalAlert: true, + provisional: true, + sound: true, + ); + + if (settings.authorizationStatus == AuthorizationStatus.authorized) { + if (kDebugMode) { + print('user granted permission'); + } + } else if (settings.authorizationStatus == + AuthorizationStatus.provisional) { + if (kDebugMode) { + print('user granted provisional permission'); + } + } else { + //appsetting.AppSettings.openNotificationSettings(); + if (kDebugMode) { + print('user denied permission'); + } + } + } + + // function to show visible notification when app is active + Future showNotification(RemoteMessage message) async { + AndroidNotificationChannel channel = AndroidNotificationChannel( + message.notification!.android!.channelId.toString(), + message.notification!.android!.channelId.toString(), + importance: Importance.max, + showBadge: true, + playSound: true, + sound: const RawResourceAndroidNotificationSound('jetsons_doorbell')); + + AndroidNotificationDetails androidNotificationDetails = + AndroidNotificationDetails( + channel.id.toString(), channel.name.toString(), + channelDescription: 'your channel description', + importance: Importance.high, + priority: Priority.high, + playSound: true, + ticker: 'ticker', + sound: channel.sound + // sound: RawResourceAndroidNotificationSound('jetsons_doorbell') + // icon: largeIconPath + ); + + const DarwinNotificationDetails darwinNotificationDetails = + DarwinNotificationDetails( + presentAlert: true, presentBadge: true, presentSound: true); + + NotificationDetails notificationDetails = NotificationDetails( + android: androidNotificationDetails, iOS: darwinNotificationDetails); + + Future.delayed(Duration.zero, () { + _flutterLocalNotificationsPlugin.show( + 0, + message.notification!.title.toString(), + message.notification!.body.toString(), + notificationDetails, + ); + }); + } + + //function to get device token on which we will send the notifications + Future getDeviceToken() async { + String? token = await messaging.getToken(); + return token!; + } + + void isTokenRefresh() async { + messaging.onTokenRefresh.listen((event) { + event.toString(); + if (kDebugMode) { + print('refresh'); + } + }); + } + + //handle tap on notification when app is in background or terminated + Future setupInteractMessage(BuildContext context) async { + // when app is terminated + RemoteMessage? initialMessage = + await FirebaseMessaging.instance.getInitialMessage(); + + if (initialMessage != null) { + handleMessage(context, initialMessage); + } + + //when app ins background + FirebaseMessaging.onMessageOpenedApp.listen((event) { + handleMessage(context, event); + }); + } + + void handleMessage(BuildContext context, RemoteMessage message) {} + + Future forgroundMessage() async { + await FirebaseMessaging.instance + .setForegroundNotificationPresentationOptions( + alert: true, + badge: true, + sound: true, + ); + } +} diff --git a/lib/provider/notification_provider.dart b/lib/provider/notification_provider.dart new file mode 100644 index 0000000..6dbcdbf --- /dev/null +++ b/lib/provider/notification_provider.dart @@ -0,0 +1,40 @@ +import 'package:dio/dio.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; + +import '../models/notification_list_response.dart'; +import '../services/api_client.dart'; +import '../services/api_urls.dart'; + +class NotificationProvider extends ChangeNotifier { + NotificationProvider() { + getNotification(); + } + + final _apiClient = ApiClient(); + List notificationList = []; + + bool _isLoading = false; + + bool get isLoading => _isLoading; + + void setLoading(bool loading) { + _isLoading = loading; + notifyListeners(); + } + + Future getNotification() async { + setLoading(true); + try { + Response response = await _apiClient.get(ApiUrls.notificationUrl); + setLoading(false); + if (response.statusCode == 200) { + final data = NotificationListResponse.fromJson(response.data); + notificationList = data.notifications ?? []; + notifyListeners(); + } + } catch (e) { + setLoading(false); + } + } +} diff --git a/lib/screens/home_screen.dart b/lib/screens/home_screen.dart index 652405a..e37b3d4 100644 --- a/lib/screens/home_screen.dart +++ b/lib/screens/home_screen.dart @@ -1,3 +1,4 @@ +import 'package:cheminova/notification_service.dart'; import 'package:cheminova/provider/user_provider.dart'; import 'package:cheminova/screens/assign_task_dash_board_screen.dart'; import 'package:cheminova/screens/calendar_screen.dart'; @@ -11,6 +12,7 @@ import 'package:cheminova/screens/product_sales_data.dart'; import 'package:cheminova/screens/update_inventory_screen.dart'; import 'package:cheminova/screens/display_sales_screen.dart'; import 'package:cheminova/widgets/common_drawer.dart'; +import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:flutter/material.dart'; import 'package:cheminova/widgets/common_background.dart'; import 'package:cheminova/screens/daily_tasks_screen.dart'; @@ -24,12 +26,19 @@ class HomePage extends StatefulWidget { } class _HomePageState extends State { + NotificationServices notificationServices = NotificationServices(); + @override void initState() { super.initState(); WidgetsBinding.instance.addPostFrameCallback((_) { Provider.of(context, listen: false).fetchUserProfile(); }); + notificationServices.requestNotificationPermission(); + + notificationServices.getDeviceToken().then((value) { + print('Device Token: $value'); + }); } @override diff --git a/lib/screens/notification_screen.dart b/lib/screens/notification_screen.dart index 1bdb703..86d1380 100644 --- a/lib/screens/notification_screen.dart +++ b/lib/screens/notification_screen.dart @@ -1,8 +1,12 @@ -import 'package:cheminova/widgets/common_background.dart'; +import 'package:cheminova/models/notification_list_response.dart'; +import 'package:cheminova/provider/notification_provider.dart'; import 'package:flutter/material.dart'; -import '../widgets/common_app_bar.dart'; -import '../widgets/common_elevated_button.dart'; -import '../widgets/common_text_form_field.dart'; +import 'package:cheminova/widgets/common_background.dart'; +import 'package:cheminova/widgets/common_drawer.dart'; +import 'package:cheminova/widgets/common_app_bar.dart'; +import 'package:cheminova/widgets/common_elevated_button.dart'; +import 'package:intl/intl.dart'; +import 'package:provider/provider.dart'; class NotificationScreen extends StatefulWidget { const NotificationScreen({super.key}); @@ -12,123 +16,118 @@ class NotificationScreen extends StatefulWidget { } class NotificationScreenState extends State { + late NotificationProvider _notificationProvider; + + @override + void initState() { + _notificationProvider = NotificationProvider(); + super.initState(); + } + @override Widget build(BuildContext context) { - - return CommonBackground( - child: Scaffold( - backgroundColor: Colors.transparent, - appBar: CommonAppBar( - actions: [ - IconButton( - onPressed: () { - Navigator.pop(context); - }, - icon: Image.asset('assets/Back_attendance.png'), - padding: const EdgeInsets.only(right: 20), - ), - ], - title: const Text('Notifications', - style: TextStyle( - fontSize: 20, - color: Colors.black, - fontWeight: FontWeight.w400, - fontFamily: 'Anek')), + return ChangeNotifierProvider( + create: (context) => _notificationProvider, + child: CommonBackground( + child: Scaffold( backgroundColor: Colors.transparent, - elevation: 0, - ), - body: SingleChildScrollView( - child: Column( - children: [ - Container( - padding: const EdgeInsets.all(20.0).copyWith(top: 20, bottom: 30), - margin: const EdgeInsets.symmetric(horizontal: 20.0), - decoration: BoxDecoration( - border: Border.all(color: Colors.white), - color: const Color(0xffB4D1E5).withOpacity(0.9), - borderRadius: BorderRadius.circular(26.0), - ), - child: const Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.start, - children: [ - Text('Notifications', - style: TextStyle( - fontSize: 30, - color: Colors.black, - fontWeight: FontWeight.w500, - fontFamily: 'Anek', - )), - SizedBox( - height: 10, - ), - SizedBox(height:15 ), - CommonTextFormField( - title: ' Notification Type:Alert', - ), - SizedBox(height: 20,), - - ]) + appBar: CommonAppBar( + actions: [ + IconButton( + onPressed: () { + Navigator.pop(context); + }, + icon: Image.asset('assets/Back_attendance.png'), + padding: const EdgeInsets.only(right: 20), ), - const SizedBox(height: 20,), - Container( - padding: const EdgeInsets.all(20.0).copyWith(top: 20, bottom: 30), - margin: const EdgeInsets.symmetric(horizontal: 20.0), - decoration: BoxDecoration( - border: Border.all(color: Colors.white), - color: const Color(0xffB4D1E5).withOpacity(0.9), - borderRadius: BorderRadius.circular(26.0), - ), - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.start, - children: [ - const Text('Notifications List', - style: TextStyle( - fontSize: 30, - color: Colors.black, - fontWeight: FontWeight.w500, - fontFamily: 'Anek', - )), - const SizedBox(height: 10), - const CommonTextFormField( - title: 'Task: Review Sales Data by EOD\nDate: 10-06-2024',), - const SizedBox(height: 20,), - Align( - alignment: Alignment.center, - child: CommonElevatedButton( - borderRadius: 30, - width: double.infinity, - height: kToolbarHeight - 10, - text: 'VIEW DETAILS', - backgroundColor: const Color(0xff004791), - onPressed: () { - }, - ), - ), - const SizedBox(height: 20,), - const CommonTextFormField( - title: 'Alert: New Dealer Assigned\nDate: 10-06-2024',), - const SizedBox(height: 15), - Align( - alignment: Alignment.center, - child: CommonElevatedButton( - borderRadius: 30, - width: double.infinity, - height: kToolbarHeight - 10, - text: 'VIEW DETAILS', - backgroundColor: const Color(0xff004791), - onPressed: () { - - }, - ), - ), - ], + ], + title: const Text('Notification', + style: TextStyle( + fontSize: 20, + color: Colors.black, + fontWeight: FontWeight.w400, + fontFamily: 'Anek')), + backgroundColor: Colors.transparent, + elevation: 0, + ), + drawer: const CommonDrawer(), + body: Consumer( + builder: (context, value, child) => value.isLoading + ? const Center(child: CircularProgressIndicator()) + : MyListView(value: value), ), ), - ]), - ))); + ), + ); + } +} + +Widget buildProductButton(String productName) { + return Padding( + padding: const EdgeInsets.only(bottom: 15), + child: CommonElevatedButton( + borderRadius: 30, + width: double.infinity, + height: kToolbarHeight - 10, + text: productName, + backgroundColor: const Color(0xff004791), + onPressed: () { + // Handle product button press + debugPrint('$productName pressed'); + }, + ), + ); +} + +class MyListView extends StatelessWidget { + final NotificationProvider value; + + const MyListView({super.key, required this.value}); + + @override + Widget build(BuildContext context) { + return value.notificationList.isEmpty + ? const Center( + child: Text( + 'No Notifications', + style: TextStyle( + fontSize: 20, + color: Colors.white, + fontWeight: FontWeight.w500, + fontFamily: 'Anek', + ), + ), + ) + : ListView.builder( + padding: const EdgeInsets.only(top: 15), + itemCount: value.notificationList.length, + itemBuilder: (context, index) { + Notifications item = value.notificationList[index]; + return Padding( + padding: const EdgeInsets.only(bottom: 10, left: 10, right: 10), + child: ExpansionTile( + collapsedBackgroundColor: Colors.white, + backgroundColor: Colors.white, + trailing: const SizedBox.shrink(), + title: Text( + item.title ?? '', + style: const TextStyle( + fontSize: 17, fontWeight: FontWeight.w500), + ), + subtitle: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + (DateFormat("dd/MMM/yyyy").format( + DateTime.parse(item.createdAt ?? ''), + )), + ), + Text(item.msg ?? ''), + ], + ), + ), + ); + }, + ); } } diff --git a/lib/screens/rejected_application_screen.dart b/lib/screens/rejected_application_screen.dart index db7c8f5..abc96bb 100644 --- a/lib/screens/rejected_application_screen.dart +++ b/lib/screens/rejected_application_screen.dart @@ -13,7 +13,8 @@ class RejectedApplicationScreen extends StatefulWidget { const RejectedApplicationScreen({super.key}); @override - State createState() => _RejectedApplicationScreenState(); + State createState() => + _RejectedApplicationScreenState(); } class _RejectedApplicationScreenState extends State { @@ -56,66 +57,33 @@ class _RejectedApplicationScreenState extends State { builder: (context, value, child) => value.isLoading ? const Center(child: CircularProgressIndicator()) : MyListView(value: value), - // child: Padding( - // padding: const EdgeInsets.all(16.0), - // child: SingleChildScrollView( - // physics: const BouncingScrollPhysics(), - // child: Column( - // mainAxisAlignment: MainAxisAlignment.center, - // crossAxisAlignment: CrossAxisAlignment.center, - // children: [ - // const SizedBox(height: 16), - // Container( - // padding: const EdgeInsets.all(20.0).copyWith( - // top: 30, bottom: 30), - // decoration: BoxDecoration( - // border: Border.all(color: Colors.white), - // color: const Color(0xffB4D1E5).withOpacity(0.9), - // borderRadius: BorderRadius.circular(26.0), - // ), - // child: Column( - // crossAxisAlignment: CrossAxisAlignment.start, - // children: [ - // _buildProductButton('Trade Name 1'), - // _buildProductButton('Trade Name 2'), - // _buildProductButton('Trade Name 3'), - // // _buildProductButton('Product 4'), - // // _buildProductButton('Product 5'), - // // _buildProductButton('Product 6'), - // // _buildProductButton('Product 7'), - // ], - // ), - // ), - // ], - // ), - // ), - // ), ), ), - ) - ); + )); } } - Widget _buildProductButton(String productName) { - return Padding( - padding: const EdgeInsets.only(bottom: 15), - child: CommonElevatedButton( - borderRadius: 30, - width: double.infinity, - height: kToolbarHeight - 10, - text: productName, - backgroundColor: const Color(0xff004791), - onPressed: () { - // Handle product button press - debugPrint('$productName pressed'); - }, - ), - ); - } + +Widget buildProductButton(String productName) { + return Padding( + padding: const EdgeInsets.only(bottom: 15), + child: CommonElevatedButton( + borderRadius: 30, + width: double.infinity, + height: kToolbarHeight - 10, + text: productName, + backgroundColor: const Color(0xff004791), + onPressed: () { + // Handle product button press + debugPrint('$productName pressed'); + }, + ), + ); +} class MyListView extends StatelessWidget { final RejectedProvider value; - const MyListView( {super.key, required this.value}); + + const MyListView({super.key, required this.value}); @override Widget build(BuildContext context) { @@ -123,21 +91,23 @@ class MyListView extends StatelessWidget { padding: const EdgeInsets.only(top: 15), itemCount: value.rejectedApplicationList.length, itemBuilder: (context, index) { - RejectedApplicationResponse item = value.rejectedApplicationList[index]; + RejectedApplicationResponse item = value.rejectedApplicationList[index]; return Padding( - padding: const EdgeInsets.only(bottom: 10,left: 10,right: 10), + padding: const EdgeInsets.only(bottom: 10, left: 10, right: 10), child: ExpansionTile( - collapsedBackgroundColor: Colors.white, backgroundColor: Colors.white, - - title: Text(item.tradeName ?? '',style: const TextStyle(fontSize: 17,fontWeight:FontWeight.w500),), + title: Text( + item.tradeName ?? '', + style: const TextStyle(fontSize: 17, fontWeight: FontWeight.w500), + ), subtitle: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text((DateFormat("dd/MM/yyyy") + Text((DateFormat("dd/MMMM/yyyy") .format(DateTime.parse(item.createdAt ?? '')))), Text(item.sId ?? ''), + for (var note in item.notes!) Text(note.message ?? ''), ], ), children: [ @@ -160,31 +130,35 @@ class MyListView extends StatelessWidget { title: Text('Status: ${item.status ?? ''}'), ), ListTile( - title: Text('Principal Distributor: ${item.principalDistributer!.name ?? ''}'), + title: Text( + 'Principal Distributor: ${item.principalDistributer!.name ?? ''}'), ), ListTile( title: Text('PAN Number: ${item.panNumber ?? ''}'), ), - Image.network(item.panImg!.url ?? '',height: 250,width: 250), + Image.network(item.panImg!.url ?? '', height: 250, width: 250), ListTile( - title: Text('Aadhar Number: ${item.aadharNumber?? ''}'), + title: Text('Aadhar Number: ${item.aadharNumber ?? ''}'), ), - Image.network(item.aadharImg?.url ?? '',height: 250,width: 250), + Image.network(item.aadharImg?.url ?? '', height: 250, width: 250), ListTile( title: Text('GST Number: ${item.gstNumber ?? ''}'), ), - Image.network(item.gstImg!.url ?? '',height: 250,width: 250),const ListTile( + Image.network(item.gstImg!.url ?? '', height: 250, width: 250), + const ListTile( title: Text('Pesticide License: '), ), if (item.pesticideLicenseImg != null) - Image.network(item.pesticideLicenseImg!.url ?? '',height: 250,width: 250), + Image.network(item.pesticideLicenseImg!.url ?? '', + height: 250, width: 250), // if (item['fertilizer_license_img'] != null) // Image.network(item['fertilizer_license_img']['url'] ?? ''), const ListTile( title: Text('selfieEntranceImg: '), ), - Image.network(item.selfieEntranceImg!.url ?? '',height: 250,width: 250), + Image.network(item.selfieEntranceImg!.url ?? '', + height: 250, width: 250), const ListTile( title: Text('Notes:'), ), @@ -194,11 +168,10 @@ class MyListView extends StatelessWidget { contentPadding: const EdgeInsets.only(left: 20), title: Text(note.message ?? ''), ), - ], ), ); }, ); } -} \ No newline at end of file +} diff --git a/lib/services/api_urls.dart b/lib/services/api_urls.dart index d9a943a..10a12b1 100644 --- a/lib/services/api_urls.dart +++ b/lib/services/api_urls.dart @@ -9,4 +9,5 @@ class ApiUrls { static const String createCollectKycUrl = '${baseUrl}kyc/create-tm'; static const String getPdUrl = 'kyc/get-pd-tm'; static const String rejectedApplication = '${baseUrl}kyc/getAllrejected-tm'; + static const String notificationUrl = '$baseUrl/get-notification-tm'; } diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index 850b6d6..8b519a4 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -6,12 +6,18 @@ import FlutterMacOS import Foundation import file_selector_macos +import firebase_core +import firebase_messaging +import flutter_local_notifications import flutter_secure_storage_macos import geolocator_apple import path_provider_foundation func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin")) + FLTFirebaseCorePlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseCorePlugin")) + FLTFirebaseMessagingPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseMessagingPlugin")) + FlutterLocalNotificationsPlugin.register(with: registry.registrar(forPlugin: "FlutterLocalNotificationsPlugin")) FlutterSecureStoragePlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStoragePlugin")) GeolocatorPlugin.register(with: registry.registrar(forPlugin: "GeolocatorPlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) diff --git a/pubspec.lock b/pubspec.lock index 9157093..7b5b5bb 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -9,6 +9,14 @@ packages: url: "https://pub.dev" source: hosted version: "67.0.0" + _flutterfire_internals: + dependency: transitive + description: + name: _flutterfire_internals + sha256: b1595874fbc8f7a50da90f5d8f327bb0bfd6a95dc906c390efe991540c3b54aa + url: "https://pub.dev" + source: hosted + version: "1.3.40" analyzer: dependency: transitive description: @@ -209,6 +217,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.2.0" + dbus: + dependency: transitive + description: + name: dbus + sha256: "365c771ac3b0e58845f39ec6deebc76e3276aa9922b0cc60840712094d9047ac" + url: "https://pub.dev" + source: hosted + version: "0.7.10" dio: dependency: "direct main" description: @@ -281,6 +297,54 @@ packages: url: "https://pub.dev" source: hosted version: "0.9.3+2" + firebase_core: + dependency: "direct main" + description: + name: firebase_core + sha256: "3187f4f8e49968573fd7403011dca67ba95aae419bc0d8131500fae160d94f92" + url: "https://pub.dev" + source: hosted + version: "3.3.0" + firebase_core_platform_interface: + dependency: transitive + description: + name: firebase_core_platform_interface + sha256: "3c3a1e92d6f4916c32deea79c4a7587aa0e9dbbe5889c7a16afcf005a485ee02" + url: "https://pub.dev" + source: hosted + version: "5.2.0" + firebase_core_web: + dependency: transitive + description: + name: firebase_core_web + sha256: e8d1e22de72cb21cdcfc5eed7acddab3e99cd83f3b317f54f7a96c32f25fd11e + url: "https://pub.dev" + source: hosted + version: "2.17.4" + firebase_messaging: + dependency: "direct main" + description: + name: firebase_messaging + sha256: "1b0a4f9ecbaf9007771bac152afad738ddfacc4b8431a7591c00829480d99553" + url: "https://pub.dev" + source: hosted + version: "15.0.4" + firebase_messaging_platform_interface: + dependency: transitive + description: + name: firebase_messaging_platform_interface + sha256: c5a6443e66ae064fe186901d740ee7ce648ca2a6fd0484b8c5e963849ac0fc28 + url: "https://pub.dev" + source: hosted + version: "4.5.42" + firebase_messaging_web: + dependency: transitive + description: + name: firebase_messaging_web + sha256: "232ef63b986467ae5b5577a09c2502b26e2e2aebab5b85e6c966a5ca9b038b89" + url: "https://pub.dev" + source: hosted + version: "3.8.12" fixnum: dependency: transitive description: @@ -326,6 +390,30 @@ packages: url: "https://pub.dev" source: hosted version: "3.0.2" + flutter_local_notifications: + dependency: "direct main" + description: + name: flutter_local_notifications + sha256: dd6676d8c2926537eccdf9f72128bbb2a9d0814689527b17f92c248ff192eaf3 + url: "https://pub.dev" + source: hosted + version: "17.2.1+2" + 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_plugin_android_lifecycle: dependency: transitive description: @@ -1021,6 +1109,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.4" + timezone: + dependency: transitive + description: + name: timezone + sha256: "2236ec079a174ce07434e89fcd3fcda430025eb7692244139a9cf54fdcf1fc7d" + url: "https://pub.dev" + source: hosted + version: "0.9.4" timing: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 9e24874..65f9faa 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -45,6 +45,9 @@ dependencies: geocoding: ^3.0.0 pretty_dio_logger: ^1.3.1 csc_picker: ^0.2.7 + firebase_core: ^3.3.0 + firebase_messaging: ^15.0.4 + flutter_local_notifications: ^17.2.1+2 dev_dependencies: flutter_test: diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc index 6a1b9be..41dfcd2 100644 --- a/windows/flutter/generated_plugin_registrant.cc +++ b/windows/flutter/generated_plugin_registrant.cc @@ -7,6 +7,7 @@ #include "generated_plugin_registrant.h" #include +#include #include #include #include @@ -14,6 +15,8 @@ void RegisterPlugins(flutter::PluginRegistry* registry) { FileSelectorWindowsRegisterWithRegistrar( registry->GetRegistrarForPlugin("FileSelectorWindows")); + FirebaseCorePluginCApiRegisterWithRegistrar( + registry->GetRegistrarForPlugin("FirebaseCorePluginCApi")); FlutterSecureStorageWindowsPluginRegisterWithRegistrar( registry->GetRegistrarForPlugin("FlutterSecureStorageWindowsPlugin")); GeolocatorWindowsRegisterWithRegistrar( diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index e55e969..9c3f356 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -4,6 +4,7 @@ list(APPEND FLUTTER_PLUGIN_LIST file_selector_windows + firebase_core flutter_secure_storage_windows geolocator_windows permission_handler_windows