1)Push notification added
This commit is contained in:
parent
e40371ad6a
commit
410c8903ae
@ -19,6 +19,10 @@ pluginManagement {
|
|||||||
plugins {
|
plugins {
|
||||||
id "dev.flutter.flutter-plugin-loader" version "1.0.0"
|
id "dev.flutter.flutter-plugin-loader" version "1.0.0"
|
||||||
id "com.android.application" version "7.3.0" apply false
|
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
|
||||||
|
id "com.google.firebase.crashlytics" version "2.8.1" apply false
|
||||||
|
// END: FlutterFire Configuration
|
||||||
id "org.jetbrains.kotlin.android" version "1.7.10" apply false
|
id "org.jetbrains.kotlin.android" version "1.7.10" apply false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
36
lib/controller/notification_api_service.dart
Normal file
36
lib/controller/notification_api_service.dart
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
import 'package:cheminova/models/notification_model.dart';
|
||||||
|
import 'package:cheminova/utils/api_urls.dart';
|
||||||
|
|
||||||
|
import '../utils/common_api_service.dart';
|
||||||
|
|
||||||
|
class NotificationApiService {
|
||||||
|
Future<List<NotificationModel>?> getNotification(String token) async {
|
||||||
|
try {
|
||||||
|
String url =ApiUrls.getNotificationUrl; // Base URL to fetch product manuals
|
||||||
|
|
||||||
|
final response = await commonApiService<List<NotificationModel>>(
|
||||||
|
method: "GET",
|
||||||
|
url: url,
|
||||||
|
additionalHeaders: { // Pass the token here
|
||||||
|
'Authorization': 'Bearer $token',
|
||||||
|
},
|
||||||
|
fromJson: (json) {
|
||||||
|
if (json['notifications'] != null) {
|
||||||
|
// Map the list of product manuals from the response
|
||||||
|
final List<NotificationModel> notification = (json['notifications'] as List)
|
||||||
|
.map((manualJson) => NotificationModel.fromJson(manualJson as Map<String, dynamic>))
|
||||||
|
.toList();
|
||||||
|
return notification;
|
||||||
|
} else {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
return response;
|
||||||
|
} catch (e) {
|
||||||
|
|
||||||
|
print(e.toString());
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
50
lib/controller/notification_controller.dart
Normal file
50
lib/controller/notification_controller.dart
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
import 'package:cheminova/controller/notification_api_service.dart';
|
||||||
|
import 'package:cheminova/controller/product_mannual_service.dart';
|
||||||
|
import 'package:cheminova/models/notification_model.dart';
|
||||||
|
import 'package:cheminova/notification_service.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
import '../models/product_mannual_model.dart'; // Your model import
|
||||||
|
// Your service import
|
||||||
|
|
||||||
|
class NotificationController extends GetxController {
|
||||||
|
|
||||||
|
var notificationList = <NotificationModel>[].obs;
|
||||||
|
|
||||||
|
// Service to fetch data
|
||||||
|
final NotificationApiService notificationApiService = NotificationApiService();
|
||||||
|
|
||||||
|
// Loading state
|
||||||
|
var isLoading = false.obs;
|
||||||
|
|
||||||
|
// Method to fetch product manuals from the service
|
||||||
|
void fetchNotificationApiService() async {
|
||||||
|
try {
|
||||||
|
// Set loading to true
|
||||||
|
isLoading.value = true;
|
||||||
|
SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||||
|
String? token = prefs.getString('token');
|
||||||
|
var manuals = await notificationApiService.getNotification(token!);
|
||||||
|
|
||||||
|
// If data is returned, update the list
|
||||||
|
if (manuals != null) {
|
||||||
|
notificationList .value = manuals;
|
||||||
|
} else {
|
||||||
|
notificationList.value = []; // If no data, set an empty list
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
// Handle error here, for example logging or showing an error message
|
||||||
|
print("Error fetching product manuals: $e");
|
||||||
|
} finally {
|
||||||
|
// Set loading to false
|
||||||
|
isLoading.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onInit() {
|
||||||
|
// Fetch product manuals when the controller is initialized
|
||||||
|
fetchNotificationApiService();
|
||||||
|
super.onInit();
|
||||||
|
}
|
||||||
|
}
|
@ -1,12 +1,14 @@
|
|||||||
|
|
||||||
|
|
||||||
|
import 'package:cheminova/utils/api_urls.dart';
|
||||||
|
|
||||||
import '../models/product_mannual_model.dart';
|
import '../models/product_mannual_model.dart';
|
||||||
import '../utils/common_api_service.dart'; // Replace with your actual common API service import
|
import '../utils/common_api_service.dart'; // Replace with your actual common API service import
|
||||||
|
|
||||||
class ProductMannualService {
|
class ProductMannualService {
|
||||||
Future<List<ProductManualModel>?> getProductManuals(String token) async {
|
Future<List<ProductManualModel>?> getProductManuals(String token) async {
|
||||||
try {
|
try {
|
||||||
String url = "/api/productmanual/getall"; // Base URL to fetch product manuals
|
String url = ApiUrls.getProductManualUrl; // Base URL to fetch product manuals
|
||||||
|
|
||||||
final response = await commonApiService<List<ProductManualModel>>(
|
final response = await commonApiService<List<ProductManualModel>>(
|
||||||
method: "GET",
|
method: "GET",
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
import 'package:cheminova/utils/api_urls.dart';
|
||||||
|
|
||||||
import '../models/product_model1.dart';
|
import '../models/product_model1.dart';
|
||||||
import '../utils/common_api_service.dart';
|
import '../utils/common_api_service.dart';
|
||||||
import '../utils/show_snackbar.dart';
|
import '../utils/show_snackbar.dart';
|
||||||
@ -7,7 +9,7 @@ class ProductService {
|
|||||||
try {
|
try {
|
||||||
String url;
|
String url;
|
||||||
if (category != null && category.isNotEmpty) {
|
if (category != null && category.isNotEmpty) {
|
||||||
url = "/api/product/getAll/user?page=$page&category=$category";
|
url = "ApiUrls.getProductUrl?page=$page&category=$category";
|
||||||
} else {
|
} else {
|
||||||
url = "/api/product/getAll/user?page=$page"; // URL without category filter
|
url = "/api/product/getAll/user?page=$page"; // URL without category filter
|
||||||
}
|
}
|
||||||
@ -39,7 +41,7 @@ class ProductService {
|
|||||||
try {
|
try {
|
||||||
final response = await commonApiService<List<Map<String, dynamic>>>(
|
final response = await commonApiService<List<Map<String, dynamic>>>(
|
||||||
method: "GET",
|
method: "GET",
|
||||||
url: "/api/category/getCategories",
|
url: ApiUrls.getCategoryUrl,
|
||||||
fromJson: (json) {
|
fromJson: (json) {
|
||||||
if (json['categories'] != null) {
|
if (json['categories'] != null) {
|
||||||
final List<Map<String, dynamic>> category = (json['categories'] as List)
|
final List<Map<String, dynamic>> category = (json['categories'] as List)
|
||||||
|
@ -1,8 +1,23 @@
|
|||||||
import 'package:cheminova/screens/splash_screen.dart';
|
import 'package:cheminova/screens/splash_screen.dart';
|
||||||
|
import 'package:firebase_core/firebase_core.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
|
import 'package:hive_flutter/adapters.dart';
|
||||||
|
|
||||||
|
import 'firebase_options.dart';
|
||||||
|
|
||||||
|
var box;
|
||||||
void main()async{
|
void main()async{
|
||||||
|
|
||||||
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
await Firebase.initializeApp(
|
||||||
|
options: DefaultFirebaseOptions.currentPlatform,
|
||||||
|
);
|
||||||
|
await Hive.initFlutter();
|
||||||
|
await Hive.openBox('cartBox');
|
||||||
runApp(const MyApp());
|
runApp(const MyApp());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
67
lib/models/notification_model.dart
Normal file
67
lib/models/notification_model.dart
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
class NotificationModel {
|
||||||
|
String? id;
|
||||||
|
String? title;
|
||||||
|
String? msg;
|
||||||
|
String? addedFor;
|
||||||
|
String? createdAt;
|
||||||
|
String? updatedAt;
|
||||||
|
int? v;
|
||||||
|
|
||||||
|
NotificationModel({
|
||||||
|
this.id,
|
||||||
|
this.title,
|
||||||
|
this.msg,
|
||||||
|
this.addedFor,
|
||||||
|
this.createdAt,
|
||||||
|
this.updatedAt,
|
||||||
|
this.v,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory NotificationModel.fromJson(Map<String, dynamic> json) {
|
||||||
|
return NotificationModel(
|
||||||
|
id: json['_id'],
|
||||||
|
title: json['title'],
|
||||||
|
msg: json['msg'],
|
||||||
|
addedFor: json['added_for'],
|
||||||
|
createdAt: json['createdAt'],
|
||||||
|
updatedAt: json['updatedAt'],
|
||||||
|
v: json['__v'],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
return {
|
||||||
|
'_id': id,
|
||||||
|
'title': title,
|
||||||
|
'msg': msg,
|
||||||
|
'added_for': addedFor,
|
||||||
|
'createdAt': createdAt,
|
||||||
|
'updatedAt': updatedAt,
|
||||||
|
'__v': v,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class NotificationResponse {
|
||||||
|
String? returnMessage;
|
||||||
|
List<NotificationModel>? notifications;
|
||||||
|
|
||||||
|
NotificationResponse({this.returnMessage, this.notifications});
|
||||||
|
|
||||||
|
factory NotificationResponse.fromJson(Map<String, dynamic> json) {
|
||||||
|
return NotificationResponse(
|
||||||
|
returnMessage: json['return_message'],
|
||||||
|
notifications: json['notifications'] != null
|
||||||
|
? List<NotificationModel>.from(json['notifications']
|
||||||
|
.map((notification) => NotificationModel.fromJson(notification)))
|
||||||
|
: null,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
return {
|
||||||
|
'return_message': returnMessage,
|
||||||
|
'notifications': notifications?.map((notification) => notification.toJson()).toList(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
165
lib/notification_service.dart
Normal file
165
lib/notification_service.dart
Normal file
@ -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<void> 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<String> 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<void> 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,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -1,8 +1,11 @@
|
|||||||
import 'package:cheminova/controller/home_controller.dart';
|
import 'package:cheminova/controller/home_controller.dart';
|
||||||
import 'package:cheminova/screens/inventory/inventory_management_screen.dart';
|
import 'package:cheminova/screens/inventory/inventory_management_screen.dart';
|
||||||
|
import 'package:cheminova/screens/kyc/kyc_screen.dart';
|
||||||
|
import 'package:cheminova/screens/notification/notification_screen.dart';
|
||||||
import 'package:cheminova/screens/order/order_tracking_screen.dart';
|
import 'package:cheminova/screens/order/order_tracking_screen.dart';
|
||||||
import 'package:cheminova/screens/order_management/order_management_screen.dart';
|
import 'package:cheminova/screens/order_management/order_management_screen.dart';
|
||||||
import 'package:cheminova/screens/product/product_catalog_screen.dart';
|
import 'package:cheminova/screens/product/product_catalog_screen.dart';
|
||||||
|
import 'package:cheminova/screens/product/product_mannual.dart';
|
||||||
import 'package:cheminova/screens/report/order_history_report_screen.dart';
|
import 'package:cheminova/screens/report/order_history_report_screen.dart';
|
||||||
import 'package:cheminova/screens/report/reporting_analytics_screen.dart';
|
import 'package:cheminova/screens/report/reporting_analytics_screen.dart';
|
||||||
import 'package:cheminova/screens/retail/retail_distributer_on_boarding_screen.dart';
|
import 'package:cheminova/screens/retail/retail_distributer_on_boarding_screen.dart';
|
||||||
@ -13,6 +16,8 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter_svg/flutter_svg.dart';
|
import 'package:flutter_svg/flutter_svg.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
|
|
||||||
|
import 'kyc/kyc_retailer_info_screen.dart';
|
||||||
|
|
||||||
class HomeScreen extends StatefulWidget {
|
class HomeScreen extends StatefulWidget {
|
||||||
const HomeScreen({super.key});
|
const HomeScreen({super.key});
|
||||||
|
|
||||||
@ -46,8 +51,23 @@ class _HomeScreenState extends State<HomeScreen> {
|
|||||||
title: const Text(
|
title: const Text(
|
||||||
"Welcome",
|
"Welcome",
|
||||||
),
|
),
|
||||||
|
actions: [
|
||||||
|
GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
// Action for notification icon tap
|
||||||
|
Get.to(() => NotificationScreen());
|
||||||
|
},
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.only(right: 16.0), // Add padding to align with the AppBar edges
|
||||||
|
child: const Icon(
|
||||||
|
Icons.notifications, // Notification icon
|
||||||
|
color: Colors.white, // Icon color (customize as needed)
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
drawer: const MyDrawer(),
|
drawer: MyDrawer(),
|
||||||
body: Stack(
|
body: Stack(
|
||||||
fit: StackFit.expand,
|
fit: StackFit.expand,
|
||||||
children: [
|
children: [
|
||||||
@ -59,80 +79,106 @@ class _HomeScreenState extends State<HomeScreen> {
|
|||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.all(8.0),
|
padding: const EdgeInsets.all(8.0),
|
||||||
child: SingleChildScrollView(
|
child: SingleChildScrollView(
|
||||||
child: Column(
|
child: Padding(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
padding: const EdgeInsets.only(left: 15.0,),
|
||||||
children: [
|
child: Column(
|
||||||
Row(
|
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
children: [
|
||||||
children: [
|
Row(
|
||||||
HomeCard(
|
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||||
title: 'Product Catalogue',
|
children: [
|
||||||
onTap: () =>
|
HomeCard(
|
||||||
Get.to(() => const ProductCatalogScreen()),
|
title: 'Product Catalogue',
|
||||||
),
|
onTap: () =>
|
||||||
HomeCard(
|
Get.to(() => const ProductCatalogScreen()),
|
||||||
title: 'Order Tracking',
|
|
||||||
onTap: () => Get.to(
|
|
||||||
() => const OrderTrackingScreen(),
|
|
||||||
),
|
),
|
||||||
),
|
HomeCard(
|
||||||
],
|
title: 'Order Tracking',
|
||||||
),
|
onTap: () => Get.to(
|
||||||
const SizedBox(height: 10),
|
() => const OrderTrackingScreen(),
|
||||||
Row(
|
),
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
|
||||||
children: [
|
|
||||||
HomeCard(
|
|
||||||
title: 'Order Management',
|
|
||||||
onTap: () => Get.to(
|
|
||||||
() => OrderManagementScreen(),
|
|
||||||
),
|
),
|
||||||
),
|
],
|
||||||
HomeCard(
|
),
|
||||||
title: 'Shipping Management',
|
const SizedBox(height: 10),
|
||||||
onTap: () => Get.to(
|
Row(
|
||||||
() => const ShippingManagementScreen(),
|
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||||
|
children: [
|
||||||
|
HomeCard(
|
||||||
|
title: 'Order Management',
|
||||||
|
onTap: () => Get.to(
|
||||||
|
() => OrderManagementScreen(),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
HomeCard(
|
||||||
],
|
title: 'Shipping Management',
|
||||||
),
|
onTap: () => Get.to(
|
||||||
const SizedBox(height: 10),
|
() => const ShippingManagementScreen(),
|
||||||
Row(
|
),
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
|
||||||
children: [
|
|
||||||
HomeCard(
|
|
||||||
title: 'Inventory Management',
|
|
||||||
onTap: () => Get.to(
|
|
||||||
() => const InventoryManagementScreen(),
|
|
||||||
),
|
),
|
||||||
),
|
],
|
||||||
HomeCard(
|
),
|
||||||
title: 'Reporting & Analytics',
|
const SizedBox(height: 10),
|
||||||
onTap: () => Get.to(
|
Row(
|
||||||
() => const ReportingAnalyticsScreen(),
|
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||||
|
children: [
|
||||||
|
HomeCard(
|
||||||
|
title: 'Inventory Management',
|
||||||
|
onTap: () => Get.to(
|
||||||
|
() => const InventoryManagementScreen(),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
HomeCard(
|
||||||
],
|
title: 'Reporting & Analytics',
|
||||||
),
|
onTap: () => Get.to(
|
||||||
const SizedBox(height: 10),
|
() => const ReportingAnalyticsScreen(),
|
||||||
Row(
|
),
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
|
||||||
children: [
|
|
||||||
HomeCard(
|
|
||||||
title: 'Order Data Export',
|
|
||||||
onTap: () => Get.to(
|
|
||||||
() => const OrderHistoryReportScreen(),
|
|
||||||
),
|
),
|
||||||
),
|
|
||||||
HomeCard(
|
],
|
||||||
title: 'Retail Distributors Onboarding',
|
),
|
||||||
onTap: () => Get.to(
|
const SizedBox(height: 10),
|
||||||
() => const RetailDistributerOnBoardingScreen(),
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||||
|
children: [
|
||||||
|
HomeCard(
|
||||||
|
title: 'Order Data Export',
|
||||||
|
onTap: () => Get.to(
|
||||||
|
() => const OrderHistoryReportScreen(),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
HomeCard(
|
||||||
],
|
title: 'Retail Distributors Onboarding',
|
||||||
),
|
onTap: () => Get.to(
|
||||||
],
|
() => const RetailDistributerOnBoardingScreen(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
|
||||||
|
),
|
||||||
|
const SizedBox(height: 10),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||||
|
children: [
|
||||||
|
HomeCard(
|
||||||
|
title: 'Product Mannual',
|
||||||
|
onTap: () => Get.to(
|
||||||
|
() => const ProductsManualScreen(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
HomeCard(
|
||||||
|
title: 'Kyc',
|
||||||
|
onTap: () => Get.to(
|
||||||
|
() => KycRetailerInfoScreen(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
|
||||||
|
],
|
||||||
|
),
|
||||||
|
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -67,7 +67,7 @@ class _InventoryManagementScreenState extends State<InventoryManagementScreen> {
|
|||||||
"Inventory Management",
|
"Inventory Management",
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
drawer: const MyDrawer(),
|
drawer: MyDrawer(),
|
||||||
body: Stack(
|
body: Stack(
|
||||||
fit: StackFit.expand,
|
fit: StackFit.expand,
|
||||||
children: [
|
children: [
|
||||||
|
131
lib/screens/notification/notification_screen.dart
Normal file
131
lib/screens/notification/notification_screen.dart
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
import 'package:cheminova/controller/notification_controller.dart';
|
||||||
|
import 'package:cheminova/models/notification_model.dart';
|
||||||
|
import 'package:cheminova/widgets/my_drawer.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_svg/svg.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:intl/intl.dart';
|
||||||
|
|
||||||
|
import '../../widgets/comman_background.dart';
|
||||||
|
import '../../widgets/common_appbar.dart';
|
||||||
|
|
||||||
|
class NotificationScreen extends StatelessWidget {
|
||||||
|
NotificationScreen({super.key});
|
||||||
|
|
||||||
|
// Initialize the controller
|
||||||
|
final NotificationController notificationController = Get.put(NotificationController());
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
// Fetch notifications when the screen is first built
|
||||||
|
notificationController.fetchNotificationApiService();
|
||||||
|
|
||||||
|
return CommonBackground(
|
||||||
|
child: Scaffold(
|
||||||
|
backgroundColor: Colors.transparent,
|
||||||
|
appBar: CommonAppBar(
|
||||||
|
actions: [
|
||||||
|
IconButton(
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.pop(context);
|
||||||
|
},
|
||||||
|
icon: SvgPicture.asset('assets/svg/back_arrow.svg'),
|
||||||
|
padding: const EdgeInsets.only(right: 20),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
title: const Text('Notification',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 20,
|
||||||
|
color: Colors.black,
|
||||||
|
fontWeight: FontWeight.w400,
|
||||||
|
fontFamily: 'Anek')),
|
||||||
|
backgroundColor: Colors.transparent,
|
||||||
|
elevation: 0,
|
||||||
|
),
|
||||||
|
drawer: MyDrawer(),
|
||||||
|
body: Obx(() {
|
||||||
|
// Show a loading indicator while data is being fetched
|
||||||
|
if (notificationController.isLoading.value) {
|
||||||
|
return const Center(child: CircularProgressIndicator());
|
||||||
|
} else if (notificationController.notificationList.isEmpty) {
|
||||||
|
// Handle empty state
|
||||||
|
return const Center(child: Text("No notifications available"));
|
||||||
|
} else {
|
||||||
|
// Show the notification list once data is fetched
|
||||||
|
return MyListView(notificationList: notificationController.notificationList);
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class MyListView extends StatelessWidget {
|
||||||
|
final List<NotificationModel> notificationList;
|
||||||
|
|
||||||
|
const MyListView({super.key, required this.notificationList});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
// Group notifications by date
|
||||||
|
Map<String, List<NotificationModel>> groupedNotifications = {};
|
||||||
|
|
||||||
|
for (var notification in notificationList) {
|
||||||
|
// Ensure that 'notifications' is not null
|
||||||
|
if (notification != null) {
|
||||||
|
String date = DateFormat("dd MMM yyyy")
|
||||||
|
.format(DateTime.parse(notification.createdAt ?? ''));
|
||||||
|
if (!groupedNotifications.containsKey(date)) {
|
||||||
|
groupedNotifications[date] = [];
|
||||||
|
}
|
||||||
|
groupedNotifications[date]!.add(notification);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ListView.builder(
|
||||||
|
padding: const EdgeInsets.only(top: 15),
|
||||||
|
itemCount: groupedNotifications.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
String date = groupedNotifications.keys.elementAt(index);
|
||||||
|
List<NotificationModel> notificationsForDate = groupedNotifications[date]!;
|
||||||
|
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.only(bottom: 10, left: 10, right: 10),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
// Display the date once
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(bottom: 8.0),
|
||||||
|
child: Text(
|
||||||
|
date,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 16, fontWeight: FontWeight.bold),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
// Display notifications for the date
|
||||||
|
...notificationsForDate.map(
|
||||||
|
(notification) {
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.only(bottom: 10),
|
||||||
|
child: ExpansionTile(
|
||||||
|
collapsedBackgroundColor: Colors.white,
|
||||||
|
backgroundColor: Colors.white,
|
||||||
|
trailing: const SizedBox.shrink(),
|
||||||
|
title: Text(
|
||||||
|
notification.title ?? 'No title',
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 17, fontWeight: FontWeight.w500),
|
||||||
|
),
|
||||||
|
subtitle: Text(notification.msg ?? 'No message'),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
).toList(),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -204,7 +204,7 @@ class _CheckoutScreenState extends State<CheckoutScreen> {
|
|||||||
],
|
],
|
||||||
title: const Text("Checkout"),
|
title: const Text("Checkout"),
|
||||||
),
|
),
|
||||||
drawer: const MyDrawer(),
|
drawer: MyDrawer(),
|
||||||
body: Stack(
|
body: Stack(
|
||||||
fit: StackFit.expand,
|
fit: StackFit.expand,
|
||||||
children: [
|
children: [
|
||||||
|
@ -80,7 +80,7 @@ class _OrderConfermationScreenState extends State<OrderConfermationScreen> {
|
|||||||
"Order Confirmation",
|
"Order Confirmation",
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
drawer: const MyDrawer(),
|
drawer: MyDrawer(),
|
||||||
body: Stack(
|
body: Stack(
|
||||||
fit: StackFit.expand,
|
fit: StackFit.expand,
|
||||||
children: [
|
children: [
|
||||||
|
@ -61,7 +61,7 @@ class _OrderDetailScreenState extends State<OrderDetailScreen> {
|
|||||||
"Order Detail",
|
"Order Detail",
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
drawer: const MyDrawer(),
|
drawer: MyDrawer(),
|
||||||
body: Stack(
|
body: Stack(
|
||||||
fit: StackFit.expand,
|
fit: StackFit.expand,
|
||||||
children: [
|
children: [
|
||||||
|
@ -56,7 +56,7 @@ class _OrderTrackingScreenState extends State<OrderTrackingScreen> {
|
|||||||
"Order Tracking",
|
"Order Tracking",
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
drawer: const MyDrawer(),
|
drawer: MyDrawer(),
|
||||||
body: Stack(
|
body: Stack(
|
||||||
fit: StackFit.expand,
|
fit: StackFit.expand,
|
||||||
children: [
|
children: [
|
||||||
|
@ -48,7 +48,7 @@ class _ShipmentTrackingScreenState extends State<ShipmentTrackingScreen> {
|
|||||||
"Shipment Tracking",
|
"Shipment Tracking",
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
drawer: const MyDrawer(),
|
drawer: MyDrawer(),
|
||||||
body: Stack(
|
body: Stack(
|
||||||
fit: StackFit.expand,
|
fit: StackFit.expand,
|
||||||
children: [
|
children: [
|
||||||
|
@ -87,7 +87,7 @@ class _OrderManagementScreenState extends State<OrderManagementScreen> {
|
|||||||
],
|
],
|
||||||
title: const Text("Order Management"),
|
title: const Text("Order Management"),
|
||||||
),
|
),
|
||||||
drawer: const MyDrawer(),
|
drawer: MyDrawer(),
|
||||||
body: Stack(
|
body: Stack(
|
||||||
fit: StackFit.expand,
|
fit: StackFit.expand,
|
||||||
children: [
|
children: [
|
||||||
|
@ -90,7 +90,7 @@ class _CartScreenState extends State<CartScreen> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
drawer: const MyDrawer(),
|
drawer: MyDrawer(),
|
||||||
body: Stack(
|
body: Stack(
|
||||||
fit: StackFit.expand,
|
fit: StackFit.expand,
|
||||||
children: [
|
children: [
|
||||||
|
@ -175,7 +175,7 @@ class _ProductCatalogScreenState extends State<ProductCatalogScreen> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
drawer: const MyDrawer(),
|
drawer: MyDrawer(),
|
||||||
body: Stack(
|
body: Stack(
|
||||||
fit: StackFit.expand,
|
fit: StackFit.expand,
|
||||||
children: [
|
children: [
|
||||||
|
@ -64,7 +64,7 @@ class _ProductDetailScreenState extends State<ProductDetailScreen> {
|
|||||||
"Product Detail",
|
"Product Detail",
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
drawer: const MyDrawer(),
|
drawer: MyDrawer(),
|
||||||
body: Stack(
|
body: Stack(
|
||||||
fit: StackFit.expand,
|
fit: StackFit.expand,
|
||||||
children: [
|
children: [
|
||||||
|
@ -46,6 +46,7 @@ class _ProductsManualScreenState extends State<ProductsManualScreen> {
|
|||||||
return AppBar(
|
return AppBar(
|
||||||
backgroundColor: Colors.transparent,
|
backgroundColor: Colors.transparent,
|
||||||
elevation: 0,
|
elevation: 0,
|
||||||
|
title: Center(child: Text("Product Manual")),
|
||||||
leading: GestureDetector(
|
leading: GestureDetector(
|
||||||
onTap: () => Get.back(),
|
onTap: () => Get.back(),
|
||||||
child: Padding(
|
child: Padding(
|
||||||
|
@ -50,6 +50,7 @@ Future<BodyType?> commonApiService<BodyType>({
|
|||||||
if (method == "POST") {
|
if (method == "POST") {
|
||||||
response = await dio.post("$baseUrl$url",
|
response = await dio.post("$baseUrl$url",
|
||||||
data: isformData ? formData : body, options: options);
|
data: isformData ? formData : body, options: options);
|
||||||
|
|
||||||
} else if (method == "PUT") {
|
} else if (method == "PUT") {
|
||||||
response = await dio.put("$baseUrl$url",
|
response = await dio.put("$baseUrl$url",
|
||||||
data: isformData ? formData : body, options: options);
|
data: isformData ? formData : body, options: options);
|
||||||
@ -69,13 +70,14 @@ Future<BodyType?> commonApiService<BodyType>({
|
|||||||
prefs.setString('token', response.data['token']);
|
prefs.setString('token', response.data['token']);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (url == "/api/territorymanager/my-profile") {
|
// if (url == "/api/territorymanager/my-profile") {
|
||||||
return fromJson(response.data['myData']);
|
// return fromJson(response.data['myData']);
|
||||||
}
|
// }
|
||||||
|
|
||||||
return fromJson(response.data);
|
return fromJson(response.data);
|
||||||
} on DioException catch (e) {
|
} on DioException catch (e) {
|
||||||
print("dio exception $url ${e.response?.data}}");
|
print("dio exception $url ${e.response?.data}}");
|
||||||
|
|
||||||
print("dio exception details: ${e.message} ${e.response?.statusCode}");
|
print("dio exception details: ${e.message} ${e.response?.statusCode}");
|
||||||
String errorMessage = "An error occurred";
|
String errorMessage = "An error occurred";
|
||||||
|
|
||||||
|
37
lib/utils/secure__storage_service.dart
Normal file
37
lib/utils/secure__storage_service.dart
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||||
|
|
||||||
|
class SecureStorageService {
|
||||||
|
// Create a private constructor
|
||||||
|
SecureStorageService._privateConstructor();
|
||||||
|
|
||||||
|
// The single instance of the class
|
||||||
|
static final SecureStorageService _instance = SecureStorageService._privateConstructor();
|
||||||
|
|
||||||
|
// The single instance of FlutterSecureStorage
|
||||||
|
final FlutterSecureStorage _storage = const FlutterSecureStorage();
|
||||||
|
|
||||||
|
// Factory constructor to return the same instance
|
||||||
|
factory SecureStorageService() {
|
||||||
|
return _instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Method to write data to secure storage
|
||||||
|
Future<void> write({required String key, required String value}) async {
|
||||||
|
await _storage.write(key: key, value: value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Method to read data from secure storage
|
||||||
|
Future<String?> read({required String key}) async {
|
||||||
|
return await _storage.read(key: key);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Method to delete data from secure storage
|
||||||
|
Future<void> delete({required String key}) async {
|
||||||
|
await _storage.delete(key: key);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Method to clear all data from secure storage
|
||||||
|
Future<void> clear() async {
|
||||||
|
await _storage.deleteAll();
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_svg/svg.dart';
|
||||||
|
|
||||||
class CommonAppBar extends StatelessWidget implements PreferredSizeWidget {
|
class CommonAppBar extends StatelessWidget implements PreferredSizeWidget {
|
||||||
final Widget title;
|
final Widget title;
|
||||||
@ -14,7 +15,9 @@ class CommonAppBar extends StatelessWidget implements PreferredSizeWidget {
|
|||||||
leading: Builder(builder: (context) {
|
leading: Builder(builder: (context) {
|
||||||
return IconButton(
|
return IconButton(
|
||||||
onPressed: () => Scaffold.of(context).openDrawer(),
|
onPressed: () => Scaffold.of(context).openDrawer(),
|
||||||
icon: const Icon(Icons.menu),
|
icon: SvgPicture.asset(
|
||||||
|
'assets/svg/menu.svg',
|
||||||
|
),
|
||||||
color: Colors.white);
|
color: Colors.white);
|
||||||
}),
|
}),
|
||||||
backgroundColor: Colors.transparent,
|
backgroundColor: Colors.transparent,
|
||||||
|
Loading…
Reference in New Issue
Block a user