nfc package update and location services improvements

main_production_upgrade
WaseemAbbasi22 2 days ago
parent e4b84bb3e0
commit 2df279aedd

@ -1,26 +1,26 @@
import 'dart:async';
import 'dart:io';
import 'dart:developer';
import 'package:flutter/material.dart';
import 'package:nfc_manager/nfc_manager.dart';
import 'package:test_sa/dashboard_latest/dashboard_view.dart';
import 'package:flutter_nfc_kit/flutter_nfc_kit.dart';
import 'package:test_sa/extensions/context_extension.dart';
import 'package:test_sa/extensions/string_extensions.dart';
import 'package:test_sa/new_views/app_style/app_color.dart';
import 'package:test_sa/views/app_style/sizing.dart';
void showNfcReader(BuildContext context, {Function(String?)? onNcfScan}) {
showModalBottomSheet(
context: context,
enableDrag: false,
isDismissible: false,
barrierColor: Colors.black.withOpacity(0.5),
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.only(topLeft: Radius.circular(12), topRight: Radius.circular(12)),
borderRadius: BorderRadius.only(
topLeft: Radius.circular(12),
topRight: Radius.circular(12),
),
),
backgroundColor: Colors.white,
builder: (context) {
return NfcLayout(
onNcfScan: onNcfScan,
);
return NfcLayout(onNcfScan: onNcfScan);
},
);
}
@ -38,123 +38,105 @@ class _NfcLayoutState extends State<NfcLayout> {
bool _reading = false;
Widget? mainWidget;
String? nfcId;
bool _nfcScanning = false;
@override
void initState() {
super.initState();
_startNfcScan();
}
NfcManager.instance.startSession(
onDiscovered: (NfcTag tag) async {
String? identifier;
try {
final dynamic tagDynamic = tag;
final Map<String, dynamic> tagData = Map<String, dynamic>.from(tagDynamic.data as Map);
if (Platform.isAndroid) {
if (tagData.containsKey('nfca')) {
final nfcaData = tagData['nfca'] as Map<dynamic, dynamic>;
if (nfcaData.containsKey('identifier')) {
final List<int> idBytes = List<int>.from(nfcaData['identifier'] as List);
identifier = idBytes.map((e) => e.toRadixString(16).padLeft(2, '0')).join('');
}
}
} else {
if (tagData.containsKey('mifare')) {
final mifareData = tagData['mifare'] as Map<dynamic, dynamic>;
if (mifareData.containsKey('identifier')) {
final List<int> idBytes = List<int>.from(mifareData['identifier'] as List);
identifier = idBytes.map((e) => e.toRadixString(16).padLeft(2, '0')).join('');
}
}
}
} catch (e) {
print('Error reading NFC tag: $e');
}
nfcId = identifier;
Future<void> _startNfcScan() async {
if (_nfcScanning) return;
_nfcScanning = true;
try {
NFCTag tag = await FlutterNfcKit.poll(
timeout: const Duration(seconds: 20),
);
nfcId = tag.id.toUpperCase();
setState(() {
_reading = true;
mainWidget = doneNfc();
// mainWidget = doneNfc();
});
await Future.delayed(const Duration(seconds: 1));
await FlutterNfcKit.finish();
Navigator.pop(context);
widget.onNcfScan?.call(nfcId);
log("NFC Tag ID: $nfcId");
} catch (e) {
if (e.toString().contains("408")) {
debugPrint("NFC scan timeout");
} else if (e.toString().contains("cancelled")) {
debugPrint("NFC cancelled by user");
} else {
debugPrint("NFC error: $e");
"Failed to read NFC card".showToast;
}
Future.delayed(const Duration(seconds: 1), () {
NfcManager.instance.stopSession();
Navigator.pop(context);
widget.onNcfScan!(nfcId!);
});
},
pollingOptions: {NfcPollingOption.iso14443},
).catchError((err) {
print(err);
});
try {
await FlutterNfcKit.finish();
} catch (_) {}
Navigator.pop(context);
} finally {
_nfcScanning = false;
}
}
@override
Widget build(BuildContext context) {
(mainWidget == null && !_reading) ? mainWidget = scanNfc() : mainWidget = doneNfc();
return AnimatedSwitcher(duration: Duration(milliseconds: 500), child: mainWidget);
return AnimatedSwitcher(duration: const Duration(milliseconds: 500), child: mainWidget);
}
Widget scanNfc() {
return Container(
color: AppColor.background(context),
key: ValueKey(1),
padding: EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom + MediaQuery.of(context).padding.bottom),
key: const ValueKey(1),
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
SizedBox(
height: 30,
),
Text(
const SizedBox(height: 30),
Text(
"Ready To Scan",
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 24,
color:AppColor.headingTextColor(context)
color: AppColor.headingTextColor(context),
),
),
SizedBox(
height: 30,
),
const SizedBox(height: 30),
Image.asset(
"assets/images/ic_nfc.png",
height: MediaQuery.of(context).size.width / 3,
color: AppColor.iconColor(context),
width: double.infinity,
),
const SizedBox(
height: 30,
),
const SizedBox(height: 30),
const Text(
"Approach an NFC Tag",
style: TextStyle(
fontSize: 18,
),
),
const SizedBox(
height: 30,
style: TextStyle(fontSize: 18),
),
const SizedBox(height: 30),
ButtonTheme(
minWidth: MediaQuery.of(context).size.width / 1.2,
height: 45.0,
buttonColor: Colors.grey[300],
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(6),
),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(6)),
child: TextButton(
onPressed: () {
NfcManager.instance.stopSession();
onPressed: () async {
try {
await FlutterNfcKit.finish();
} catch (_) {}
Navigator.pop(context);
},
// elevation: 0,
child: const Text("CANCEL"),
),
),
SizedBox(
height: 30,
),
const SizedBox(height: 30),
],
),
);
@ -163,66 +145,253 @@ class _NfcLayoutState extends State<NfcLayout> {
Widget doneNfc() {
return Container(
color: AppColor.background(context),
key: ValueKey(2),
padding: EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom + MediaQuery.of(context).padding.bottom),
key: const ValueKey(2),
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
SizedBox(
height: 30,
),
const SizedBox(height: 30),
Text(
"Successfully Scanned",
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 24,
color:AppColor.headingTextColor(context)
color: AppColor.headingTextColor(context),
),
),
SizedBox(
height: 30,
),
const SizedBox(height: 30),
Image.asset(
// "assets/icons/nfc/ic_done.png",
"assets/images/ic_done.png",
height: MediaQuery.of(context).size.width / 3,
width: double.infinity,
color: AppColor.iconColor(context),
),
SizedBox(
height: 30,
),
Text(
const SizedBox(height: 30),
const Text(
"Approach an NFC Tag",
style: TextStyle(
fontSize: 18,
),
),
SizedBox(
height: 30,
style: TextStyle(fontSize: 18),
),
const SizedBox(height: 30),
ButtonTheme(
minWidth: MediaQuery.of(context).size.width / 1.2,
height: 45.0,
buttonColor: Colors.grey[300],
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(6),
),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(6)),
child: TextButton(
// onPressed: () {
// _stream?.cancel();
// widget.onNcfScan(nfcId);
// Navigator.pop(context);
// },
onPressed: null,
// elevation: 0,
child: Text("DONE",style: TextStyle(color: context.isDark?AppColor.primary10:null),),
child: Text(
"DONE",
style: TextStyle(
color: context.isDark ? AppColor.primary10 : null,
),
),
),
),
SizedBox(
height: 30,
),
const SizedBox(height: 30),
],
),
);
}
}
// class NfcLayout extends StatefulWidget {
// final Function(String? nfcId)? onNcfScan;
//
// const NfcLayout({super.key, required this.onNcfScan});
//
// @override
// _NfcLayoutState createState() => _NfcLayoutState();
// }
//
// class _NfcLayoutState extends State<NfcLayout> {
// bool _reading = false;
// Widget? mainWidget;
// String? nfcId;
//
// @override
// void initState() {
// super.initState();
//
// NfcManager.instance.startSession(
// onDiscovered: (NfcTag tag) async {
// String? identifier;
//
// try {
// final dynamic tagDynamic = tag;
// final Map<String, dynamic> tagData = Map<String, dynamic>.from(tagDynamic.data as Map);
//
// if (Platform.isAndroid) {
// if (tagData.containsKey('nfca')) {
// final nfcaData = tagData['nfca'] as Map<dynamic, dynamic>;
// if (nfcaData.containsKey('identifier')) {
// final List<int> idBytes = List<int>.from(nfcaData['identifier'] as List);
// identifier = idBytes.map((e) => e.toRadixString(16).padLeft(2, '0')).join('');
// }
// }
// } else {
// if (tagData.containsKey('mifare')) {
// final mifareData = tagData['mifare'] as Map<dynamic, dynamic>;
// if (mifareData.containsKey('identifier')) {
// final List<int> idBytes = List<int>.from(mifareData['identifier'] as List);
// identifier = idBytes.map((e) => e.toRadixString(16).padLeft(2, '0')).join('');
// }
// }
// }
// } catch (e) {
// print('Error reading NFC tag: $e');
// }
//
// nfcId = identifier;
//
// setState(() {
// _reading = true;
// mainWidget = doneNfc();
// });
//
// Future.delayed(const Duration(seconds: 1), () {
// NfcManager.instance.stopSession();
// Navigator.pop(context);
// widget.onNcfScan!(nfcId!);
// });
// },
// pollingOptions: {NfcPollingOption.iso14443},
// ).catchError((err) {
// print(err);
// });
// }
//
// @override
// Widget build(BuildContext context) {
// (mainWidget == null && !_reading) ? mainWidget = scanNfc() : mainWidget = doneNfc();
// return AnimatedSwitcher(duration: Duration(milliseconds: 500), child: mainWidget);
// }
//
// Widget scanNfc() {
// return Container(
// color: AppColor.background(context),
// key: const ValueKey(1),
// child: Column(
// mainAxisSize: MainAxisSize.min,
// children: <Widget>[
// const SizedBox(
// height: 30,
// ),
// Text(
// "Ready To Scan",
// style: TextStyle(
// fontWeight: FontWeight.bold,
// fontSize: 24,
// color:AppColor.headingTextColor(context)
// ),
// ),
// const SizedBox(
// height: 30,
// ),
// Image.asset(
// "assets/images/ic_nfc.png",
// height: MediaQuery.of(context).size.width / 3,
// color: AppColor.iconColor(context),
// width: double.infinity,
// ),
// const SizedBox(
// height: 30,
// ),
// const Text(
// "Approach an NFC Tag",
// style: TextStyle(
// fontSize: 18,
// ),
// ),
// const SizedBox(
// height: 30,
// ),
// ButtonTheme(
// minWidth: MediaQuery.of(context).size.width / 1.2,
// height: 45.0,
// buttonColor: Colors.grey[300],
// shape: RoundedRectangleBorder(
// borderRadius: BorderRadius.circular(6),
// ),
// child: TextButton(
// onPressed: () {
// NfcManager.instance.stopSession();
// Navigator.pop(context);
// },
// // elevation: 0,
// child: const Text("CANCEL"),
// ),
// ),
// const SizedBox(
// height: 30,
// ),
// ],
// ),
// );
// }
//
// Widget doneNfc() {
// return Container(
// color: AppColor.background(context),
// key: ValueKey(2),
// child: Column(
// mainAxisSize: MainAxisSize.min,
// children: <Widget>[
// const SizedBox(
// height: 30,
// ),
// Text(
// "Successfully Scanned",
// style: TextStyle(
// fontWeight: FontWeight.bold,
// fontSize: 24,
// color:AppColor.headingTextColor(context)
// ),
// ),
// const SizedBox(
// height: 30,
// ),
// Image.asset(
// // "assets/icons/nfc/ic_done.png",
// "assets/images/ic_done.png",
// height: MediaQuery.of(context).size.width / 3,
// width: double.infinity,
// color: AppColor.iconColor(context),
// ),
// const SizedBox(
// height: 30,
// ),
// const Text(
// "Approach an NFC Tag",
// style: TextStyle(
// fontSize: 18,
// ),
// ),
// const SizedBox(
// height: 30,
// ),
// ButtonTheme(
// minWidth: MediaQuery.of(context).size.width / 1.2,
// height: 45.0,
// buttonColor: Colors.grey[300],
// shape: RoundedRectangleBorder(
// borderRadius: BorderRadius.circular(6),
// ),
// child: TextButton(
// // onPressed: () {
// // _stream?.cancel();
// // widget.onNcfScan(nfcId);
// // Navigator.pop(context);
// // },
// onPressed: null,
// // elevation: 0,
// child: Text("DONE",style: TextStyle(color: context.isDark?AppColor.primary10:null),),
// ),
// ),
// const SizedBox(
// height: 30,
// ),
// ],
// ),
// );
// }
// }

@ -20,6 +20,21 @@ class LocationUtilities {
Geolocator.isLocationServiceEnabled().then((value) => callback(value));
}
static Future<bool> isEnabledAsync() async {
return await Geolocator.isLocationServiceEnabled();
}
static Future<bool> havePermissionAsync() async {
LocationPermission permission = await Geolocator.checkPermission();
if (permission == LocationPermission.denied) {
permission = await Geolocator.requestPermission();
}
return permission == LocationPermission.always ||
permission == LocationPermission.whileInUse;
}
static bool _listeningSettingChange = true;
static void listenGPS({bool change = true, Function(bool)? onChange}) async {
@ -44,61 +59,100 @@ class LocationUtilities {
completion(isGranted);
});
}
static Future<void> getCurrentLocation(
Function(Position position, bool isMocked) callback,
Function(String error) errorCallBack,
BuildContext context,
) async {
debugPrint("📍 Fetching current location...");
static void getCurrentLocation(Function(Position position, bool isMocked) callback, Function errorCallBack, BuildContext context) {
Geolocator.getCurrentPosition(desiredAccuracy: LocationAccuracy.medium, timeLimit: const Duration(seconds: 5)).then((position) {
bool isMocked = position.isMocked;
callback(position, isMocked);
}).catchError((err) {
errorCallBack();
});
// return;
// Permission.location.isGranted.then((isGranted) {
// if (!isGranted) {
// Permission.location.request().then((granted) {
// print("granted:$granted");
// if (granted == PermissionStatus.granted) {
// Geolocator.getCurrentPosition(desiredAccuracy: LocationAccuracy.medium, timeLimit: const Duration(seconds: 5)).then((position) {
// bool isMocked = position.isMocked;
// callback(position, isMocked);
// }).catchError((err) {
// print("getCurrentPositionError:$err");
// errorCallBack();
// });
// } else {
// errorCallBack();
// }
// });
// } else {
// Geolocator.getCurrentPosition(desiredAccuracy: LocationAccuracy.medium, timeLimit: const Duration(seconds: 5)).then((position) {
// bool isMocked = position.isMocked;
// callback(position, isMocked);
// }).catchError((err) {
// print("getCurrentPositionError:$err");
// errorCallBack();
// });
// }
// });
//
// // Geolocator.getCurrentPosition(desiredAccuracy: LocationAccuracy.medium, timeLimit: const Duration(seconds: 5)).then((position) {
// // bool isMocked = position.isMocked;
// // callback(position, isMocked);
// // }).catchError((err) {
// // print("getCurrentPositionError:$err");
// // errorCallBack();
// // });
//
// // locationFun((granted) {
// // if (granted) {
// // Geolocator.getCurrentPosition(desiredAccuracy: LocationAccuracy.medium, timeLimit: const Duration(seconds: 5)).then((value) {
// // done(value);
// // }).catchError((err) {
// // print("getCurrentPositionError:$err");
// // errorCallBack();
// // });
// // } else {
// // // AppPermissions
// // }
// // }, context);
try {
final position = await Geolocator.getCurrentPosition(
desiredAccuracy: LocationAccuracy.high,
timeLimit: const Duration(seconds: 10),
);
debugPrint("✅ Location: ${position.latitude}, ${position.longitude}");
callback(position, position.isMocked);
} catch (e) {
debugPrint("❌ Primary location failed: $e");
try {
final lastPosition = await Geolocator.getLastKnownPosition();
if (lastPosition != null) {
debugPrint("⚠️ Using last known location");
callback(lastPosition, lastPosition.isMocked);
return;
}
} catch (fallbackError) {
debugPrint("❌ Fallback failed: $fallbackError");
}
String message = "Unable to determine your location";
final error = e.toString().toLowerCase();
if (error.contains("timeout")) {
message = "Location request timed out. Please try again.";
} else if (error.contains("denied")) {
message = "Location permission denied.";
} else if (error.contains("disabled")) {
message = "Location services are disabled.";
}
errorCallBack(message);
}
}
// static void getCurrentLocation(Function(Position position, bool isMocked) callback, Function errorCallBack, BuildContext context) {
// Geolocator.getCurrentPosition(desiredAccuracy: LocationAccuracy.medium, timeLimit: const Duration(seconds: 5)).then((position) {
// bool isMocked = position.isMocked;
// callback(position, isMocked);
// }).catchError((err) {
// errorCallBack();
// });
// // return;
// // Permission.location.isGranted.then((isGranted) {
// // if (!isGranted) {
// // Permission.location.request().then((granted) {
// // print("granted:$granted");
// // if (granted == PermissionStatus.granted) {
// // Geolocator.getCurrentPosition(desiredAccuracy: LocationAccuracy.medium, timeLimit: const Duration(seconds: 5)).then((position) {
// // bool isMocked = position.isMocked;
// // callback(position, isMocked);
// // }).catchError((err) {
// // print("getCurrentPositionError:$err");
// // errorCallBack();
// // });
// // } else {
// // errorCallBack();
// // }
// // });
// // } else {
// // Geolocator.getCurrentPosition(desiredAccuracy: LocationAccuracy.medium, timeLimit: const Duration(seconds: 5)).then((position) {
// // bool isMocked = position.isMocked;
// // callback(position, isMocked);
// // }).catchError((err) {
// // print("getCurrentPositionError:$err");
// // errorCallBack();
// // });
// // }
// // });
// //
// // // Geolocator.getCurrentPosition(desiredAccuracy: LocationAccuracy.medium, timeLimit: const Duration(seconds: 5)).then((position) {
// // // bool isMocked = position.isMocked;
// // // callback(position, isMocked);
// // // }).catchError((err) {
// // // print("getCurrentPositionError:$err");
// // // errorCallBack();
// // // });
// //
// // // locationFun((granted) {
// // // if (granted) {
// // // Geolocator.getCurrentPosition(desiredAccuracy: LocationAccuracy.medium, timeLimit: const Duration(seconds: 5)).then((value) {
// // // done(value);
// // // }).catchError((err) {
// // // print("getCurrentPositionError:$err");
// // // errorCallBack();
// // // });
// // // } else {
// // // // AppPermissions
// // // }
// // // }, context);
// }
}

@ -5,6 +5,7 @@ import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_nfc_kit/flutter_nfc_kit.dart';
import 'package:geolocator/geolocator.dart';
import 'package:google_api_availability/google_api_availability.dart';
import 'package:huawei_location/huawei_location.dart';
@ -16,6 +17,7 @@ import 'package:shared_preferences/shared_preferences.dart';
import 'package:test_sa/controllers/providers/api/user_provider.dart';
import 'package:test_sa/extensions/context_extension.dart';
import 'package:test_sa/extensions/int_extensions.dart';
import 'package:test_sa/extensions/string_extensions.dart';
import 'package:test_sa/extensions/text_extensions.dart';
import 'package:test_sa/extensions/widget_extensions.dart';
import 'package:test_sa/main.dart';
@ -23,6 +25,8 @@ import 'package:test_sa/new_views/app_style/app_color.dart';
import 'package:test_sa/new_views/common_widgets/app_lazy_loading.dart';
import 'package:test_sa/new_views/swipe_module/dialoge/confirm_dialog.dart';
import 'package:test_sa/new_views/swipe_module/dialoge/nfc_reader_sheet.dart';
// import 'package:test_sa/new_views/swipe_module/dialoge/nfc_reader_sheet.dart';
import 'package:test_sa/new_views/swipe_module/enums/swipe_type.dart';
import 'package:test_sa/new_views/swipe_module/models/swipe_model.dart';
import 'package:test_sa/new_views/swipe_module/swipe_success_view.dart';
@ -38,7 +42,7 @@ class SwipeGeneralUtils {
static bool get isLoading => _isLoadingVisible;
void markFakeAttendance(dynamic sourceName, String lat, String long, @required BuildContext context) async {
void markFakeAttendance(dynamic sourceName, String lat, String long, {required BuildContext context}) async {
showLoading(context);
try {
hideLoading(navigatorKey.currentState!.overlay!.context);
@ -112,20 +116,33 @@ class SwipeGeneralUtils {
}
Widget attendanceTypeCard(String title, String icon, bool isEnabled, VoidCallback onPress, BuildContext context) {
return Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: isEnabled ?context.isDark ? AppColor.neutral60 : Colors.white : AppColor.background(context),
color: isEnabled
? context.isDark
? AppColor.neutral60
: Colors.white
: AppColor.background(context),
borderRadius: BorderRadius.circular(18),
border: Border.all(color:context.isDark ? AppColor.neutral60 : Colors.white70 , width: 2),
border: Border.all(color: context.isDark ? AppColor.neutral60 : Colors.white70, width: 2),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
icon.toSvgAsset(color: isEnabled ?context.isDark ? AppColor.neutral30 : AppColor.neutral50 : Colors.grey.withOpacity(0.5)),
title.heading5(context).custom(color: isEnabled ?context.isDark ? AppColor.neutral30 : AppColor.neutral50 : Colors.grey.withOpacity(0.5)),
icon.toSvgAsset(
color: isEnabled
? context.isDark
? AppColor.neutral30
: AppColor.neutral50
: Colors.grey.withOpacity(0.5)),
title.heading5(context).custom(
color: isEnabled
? context.isDark
? AppColor.neutral30
: AppColor.neutral50
: Colors.grey.withOpacity(0.5)),
],
),
).onPress(
@ -173,67 +190,130 @@ class SwipeGeneralUtils {
return (result[Permission.location] == PermissionStatus.granted || result[Permission.locationAlways] == PermissionStatus.granted);
}
void checkHuaweiLocationPermission({required SwipeTypeEnum attendanceType, required BuildContext context}) async {
// Permission_Handler permissionHandler = PermissionHandler();
LocationUtilities.isEnabled((bool isEnabled) async {
if (isEnabled) {
LocationUtilities.havePermission((bool permission) async {
if (permission) {
getHuaweiCurrentLocation(attendanceType: attendanceType, context: context);
} else {
bool has = await requestPermissions();
if (has) {
getHuaweiCurrentLocation(attendanceType: attendanceType, context: context);
} else {
showDialog(
context: context,
builder: (BuildContext cxt) => ConfirmDialog(
message: "You need to give location permission to mark attendance",
onTap: () {
Navigator.pop(context);
},
),
);
}
}
});
} else {
Future<void> checkHuaweiLocationPermission({
required SwipeTypeEnum attendanceType,
required BuildContext context,
}) async {
try {
// Check if location service is enabled
final isEnabled = await LocationUtilities.isEnabledAsync();
if (!isEnabled) {
showDialog(
context: context,
builder: (BuildContext cxt) => ConfirmDialog(
builder: (cxt) => ConfirmDialog(
message: "You need to enable location services to mark attendance",
onTap: () async {
Navigator.pop(context);
Navigator.pop(cxt);
await Geolocator.openLocationSettings();
},
),
);
return;
}
});
// if (await permissionHandler.hasLocationPermission()) {
// getHuaweiCurrentLocation(attendanceType);
// } else {
// bool has = await requestPermissions();
// if (has) {
// getHuaweiCurrentLocation(attendanceType);
// } else {
// showDialog(
// context: context,
// builder: (BuildContext cxt) => ConfirmDialog(
// message: "You need to give location permission to mark attendance",
// onTap: () {
// Navigator.pop(context);
// },
// ),
// );
// }
// }
// Check permission
bool hasPermission = await LocationUtilities.havePermissionAsync();
// Request if not granted
if (!hasPermission) {
hasPermission = await requestPermissions();
}
if (!hasPermission) {
showDialog(
context: context,
builder: (cxt) => ConfirmDialog(
message: "You need to give location permission to mark attendance",
onTap: () {
Navigator.pop(cxt);
},
),
);
return;
}
// All good get location
getHuaweiCurrentLocation(
attendanceType: attendanceType,
context: context,
);
} catch (e) {
debugPrint("❌ Huawei location error: $e");
showDialog(
context: context,
builder: (cxt) => ConfirmDialog(
message: "Something went wrong. Please try again.",
onTap: () {
Navigator.pop(cxt);
},
),
);
}
}
// void checkHuaweiLocationPermission({required SwipeTypeEnum attendanceType, required BuildContext context}) async {
// // Permission_Handler permissionHandler = PermissionHandler();
// LocationUtilities.isEnabled((bool isEnabled) async {
// if (isEnabled) {
// LocationUtilities.havePermission((bool permission) async {
// if (permission) {
// getHuaweiCurrentLocation(attendanceType: attendanceType, context: context);
// } else {
// bool has = await requestPermissions();
// if (has) {
// getHuaweiCurrentLocation(attendanceType: attendanceType, context: context);
// } else {
// showDialog(
// context: context,
// builder: (BuildContext cxt) => ConfirmDialog(
// message: "You need to give location permission to mark attendance",
// onTap: () {
// Navigator.pop(context);
// },
// ),
// );
// }
// }
// });
// } else {
// showDialog(
// context: context,
// builder: (BuildContext cxt) => ConfirmDialog(
// message: "You need to enable location services to mark attendance",
// onTap: () async {
// Navigator.pop(context);
// await Geolocator.openLocationSettings();
// },
// ),
// );
// }
// });
//
// // if (await permissionHandler.hasLocationPermission()) {
// // getHuaweiCurrentLocation(attendanceType);
// // } else {
// // bool has = await requestPermissions();
// // if (has) {
// // getHuaweiCurrentLocation(attendanceType);
// // } else {
// // showDialog(
// // context: context,
// // builder: (BuildContext cxt) => ConfirmDialog(
// // message: "You need to give location permission to mark attendance",
// // onTap: () {
// // Navigator.pop(context);
// // },
// // ),
// // );
// // }
// // }
// }
void handleSwipeOperation({required SwipeTypeEnum swipeType, required double lat, required double long, required BuildContext context}) {
switch (swipeType) {
case SwipeTypeEnum.NFC:
log('showing nfc sheet');
handleNfcAttendance(latitude: lat, longitude: long, context: context);
return;
case SwipeTypeEnum.QR:
@ -253,61 +333,140 @@ class SwipeGeneralUtils {
UserProvider userProvider = Provider.of<UserProvider>(context, listen: false);
String qrCodeValue = await Navigator.of(context).push(
MaterialPageRoute(builder: (_) => ScanQr()),
MaterialPageRoute(builder: (_) => const ScanQr()),
) as String;
if (qrCodeValue != null) {
showLoading(context);
try {
final swipeModel = Swipe(
swipeTypeValue: SwipeTypeEnum.QR.getIntFromSwipeTypeEnum(),
value: qrCodeValue,
latitude: latitude,
longitude: longitude,
);
showLoading(context);
try {
final swipeModel = Swipe(
swipeTypeValue: SwipeTypeEnum.QR.getIntFromSwipeTypeEnum(),
value: qrCodeValue,
latitude: latitude,
longitude: longitude,
);
await userProvider.makeSwipe(model: swipeModel).then((swipeResponse) {
if (swipeResponse.isSuccess == true) {
hideLoading(context);
Navigator.pushNamed(context, SwipeSuccessView.routeName);
} else {
hideLoading(context);
showDialog(
barrierDismissible: true,
context: context,
builder: (cxt) => ConfirmDialog(
message: swipeResponse.message ?? "",
onTap: () {
Navigator.pop(context);
},
onCloseTap: () {},
),
);
}
});
} catch (ex) {
log('$ex');
hideLoading(context);
//this need to confirm where it comes..
// handleException(ex, context, null);
}
await userProvider.makeSwipe(model: swipeModel).then((swipeResponse) {
if (swipeResponse.isSuccess == true) {
hideLoading(context);
Navigator.pushNamed(context, SwipeSuccessView.routeName);
} else {
hideLoading(context);
showDialog(
barrierDismissible: true,
context: context,
builder: (cxt) => ConfirmDialog(
message: swipeResponse.message ?? "",
onTap: () {
Navigator.pop(context);
},
onCloseTap: () {},
),
);
}
});
} catch (ex) {
log('$ex');
hideLoading(context);
//this need to confirm where it comes..
// handleException(ex, context, null);
}
}
Future<void> handleNfcAttendance({double? latitude = 0, double? longitude = 0, required BuildContext context}) async {
// UserProvider _userProvider = Provider.of<UserProvider>(context,listen:false);
// if (Platform.isAndroid) {
// // Android: show custom bottom sheet with your scan/done UI
// showNfcReader(context, onNcfScan: (String? nfcId) async {
// if (nfcId == null || nfcId.isEmpty) return;
//
// try {
// await _processNfcAttendance(nfcId, latitude, longitude, context);
// } finally {
// _nfcScanning = false;
// }
// });
// }
// dart
// Add this field to the class (near the top, e.g. after the constructor)
bool _nfcScanning = false;
// dart
Future<void> handleNfcAttendance({
double? latitude = 0,
double? longitude = 0,
required BuildContext context,
}) async {
// if (_nfcScanning) return;
// _nfcScanning = true;
if (Platform.isIOS) {
readNFc(onRead: (String nfcId) async {
await _processNfcAttendance(nfcId, latitude, longitude, context);
});
} else {
showNfcReader(context, onNcfScan: (String? nfcId) async {
await _processNfcAttendance(nfcId ?? '', latitude, longitude, context);
});
try {
var availability = await FlutterNfcKit.nfcAvailability;
if (availability != NFCAvailability.available) {
"NFC is not available on this device".showToast;
return;
}
if (Platform.isAndroid) {
try {
showNfcReader(context, onNcfScan: (String? nfcId) async {
log('nfc id from sheet: $nfcId');
if (nfcId == null || nfcId.isEmpty) return;
try {
await _processNfcAttendance(nfcId, latitude, longitude, context);
} catch (e) {
// handle per-scan error if needed
}
});
} catch (e) {
debugPrint('Error showing NFC reader sheet: $e');
}
} else {
try {
NFCTag tag = await FlutterNfcKit.poll(
timeout: const Duration(seconds: 20),
iosAlertMessage: "Hold your card near the phone",
iosMultipleTagMessage: "Multiple cards detected. Please scan only one.",
);
String nfcId = tag.id.toUpperCase();
await FlutterNfcKit.finish();
await _processNfcAttendance(nfcId, latitude, longitude, context);
} catch (e) {
if (!e.toString().contains("cancelled")) {
"Failed to read NFC card".showToast;
// ScaffoldMessenger.of(context).showSnackBar(
// const SnackBar(content: Text("Failed to read NFC card")),
// );
}
try {
await FlutterNfcKit.finish();
} catch (_) {}
}
}
} catch (e) {
debugPrint("NFC error: $e");
} finally {
_nfcScanning = false;
}
}
// Future<void> handleNfcAttendance({double? latitude = 0, double? longitude = 0, required BuildContext context}) async {
// bool _nfcScanning = false;
// // UserProvider _userProvider = Provider.of<UserProvider>(context,listen:false);
//
// if (Platform.isIOS) {
// readNFc(onRead: (String nfcId) async {
// await _processNfcAttendance(nfcId, latitude, longitude, context);
// });
// } else {
// showNfcReader(context, onNcfScan: (String? nfcId) async {
// await _processNfcAttendance(nfcId ?? '', latitude, longitude, context);
// });
// }
// }
Future<void> _processNfcAttendance(
String nfcId,
double? latitude,
@ -316,7 +475,12 @@ class SwipeGeneralUtils {
) async {
showLoading(context);
try {
final swipeModel = Swipe(swipeTypeValue: SwipeTypeEnum.NFC.getIntFromSwipeTypeEnum(), value: nfcId, latitude: latitude, longitude: longitude);
final swipeModel = Swipe(
swipeTypeValue: SwipeTypeEnum.NFC.getIntFromSwipeTypeEnum(),
value: nfcId,
latitude: latitude,
longitude: longitude,
);
UserProvider userProvider = Provider.of<UserProvider>(context, listen: false);
final swipeResponse = await userProvider.makeSwipe(model: swipeModel);
@ -333,50 +497,127 @@ class SwipeGeneralUtils {
}
}
void handleSwipe({required SwipeTypeEnum swipeType, required bool isEnable, required BuildContext context}) async {
if (Platform.isAndroid && !(await isGoogleServicesAvailable())) {
checkHuaweiLocationPermission(attendanceType: swipeType, context: context);
} else {
LocationUtilities.isEnabled((bool isEnabled) {
if (isEnabled) {
LocationUtilities.havePermission((bool permission) {
if (permission) {
showLoading(context);
LocationUtilities.getCurrentLocation(
(Position position, bool isMocked) {
if (isMocked) {
hideLoading(context);
markFakeAttendance(swipeType.name, position.latitude.toString() ?? "", position.longitude.toString() ?? "", context);
} else {
hideLoading(context);
handleSwipeOperation(swipeType: swipeType, lat: position.latitude, long: position.longitude, context: context);
}
},
() {
hideLoading(context);
confirmDialog(context, "Unable to determine your location, Please make sure that your location services are turned on & working.");
},
context,
);
} else {
showInfoDialog(
message: "You need to give location permission to mark attendance",
onTap: () async {
await Geolocator.openAppSettings();
});
}
});
} else {
showInfoDialog(
message: "You need to enable location services to mark attendance",
onTap: () async {
await Geolocator.openLocationSettings();
});
}
});
Future<void> handleSwipe({
required SwipeTypeEnum swipeType,
required bool isEnable,
required BuildContext context,
}) async {
try {
if (Platform.isAndroid && !(await isGoogleServicesAvailable())) {
checkHuaweiLocationPermission(
attendanceType: swipeType,
context: context,
);
return;
}
// Check if location is enabled
final isEnabled = await LocationUtilities.isEnabledAsync();
if (!isEnabled) {
showInfoDialog(
message: "You need to enable location services to mark attendance",
onTap: () async => await Geolocator.openLocationSettings(),
);
return;
}
// Check permission
final hasPermission = await LocationUtilities.havePermissionAsync();
if (!hasPermission) {
showInfoDialog(
message: "You need to give location permission to mark attendance",
onTap: () async => await Geolocator.openAppSettings(),
);
return;
}
// Show loader
showLoading(context);
// Get location
await LocationUtilities.getCurrentLocation(
(Position position, bool isMocked) {
hideLoading(context);
if (isMocked) {
markFakeAttendance(
swipeType.name,
position.latitude.toString(),
position.longitude.toString(),
context: context,
);
} else {
handleSwipeOperation(
swipeType: swipeType,
lat: position.latitude,
long: position.longitude,
context: context,
);
}
},
(String error) {
hideLoading(context);
confirmDialog(
context,
error.isNotEmpty ? error : "Unable to determine your location. Please try again.",
);
},
context,
);
} catch (e) {
hideLoading(context);
confirmDialog(
context,
"Something went wrong. Please try again.",
);
debugPrint("❌ handleSwipe error: $e");
}
}
// void handleSwipe({required SwipeTypeEnum swipeType, required bool isEnable, required BuildContext context}) async {
// if (Platform.isAndroid && !(await isGoogleServicesAvailable())) {
// checkHuaweiLocationPermission(attendanceType: swipeType, context: context);
// } else {
// LocationUtilities.isEnabled((bool isEnabled) {
// if (isEnabled) {
// LocationUtilities.havePermission((bool permission) {
// if (permission) {
// showLoading(context);
// LocationUtilities.getCurrentLocation(
// (Position position, bool isMocked) {
// if (isMocked) {
// hideLoading(context);
// markFakeAttendance(swipeType.name, position.latitude.toString(), position.longitude.toString() ?? "", context: context);
// } else {
// hideLoading(context);
// handleSwipeOperation(swipeType: swipeType, lat: position.latitude, long: position.longitude, context: context);
// }
// },
// () {
// hideLoading(context);
// confirmDialog(context, "Unable to determine your location, Please make sure that your location services are turned on & working.");
// },
// context,
// );
// } else {
// showInfoDialog(
// message: "You need to give location permission to mark attendance",
// onTap: () async {
// await Geolocator.openAppSettings();
// });
// }
// });
// } else {
// showInfoDialog(
// message: "You need to enable location services to mark attendance",
// onTap: () async {
// await Geolocator.openLocationSettings();
// });
// }
// });
// }
// }
void showInfoDialog({required String message, VoidCallback? onTap}) {
showDialog(
context: navigatorKey.currentState!.overlay!.context,
@ -431,16 +672,15 @@ class SwipeGeneralUtils {
),
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
clipBehavior: Clip.antiAliasWithSaveLayer,
builder: (BuildContext context) =>Padding(
builder: (BuildContext context) => Padding(
padding: EdgeInsets.only(
bottom: MediaQuery.of(context).viewInsets.bottom +
MediaQuery.of(context).padding.bottom,
bottom: MediaQuery.of(context).viewInsets.bottom + MediaQuery.of(context).padding.bottom,
),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
context.translation.markAttendance.heading4(context).custom(color:context.isDark?AppColor.white50: AppColor.white936),
context.translation.markAttendance.heading4(context).custom(color: context.isDark ? AppColor.white50 : AppColor.white936),
8.height,
context.translation.selectMethodToMarkAttendance.bodyText2(context).custom(color: AppColor.neutral120),
12.height,
@ -456,39 +696,40 @@ class SwipeGeneralUtils {
);
}
//older code..
void readNFc({Function(String)? onRead}) {
NfcManager.instance.startSession(
onDiscovered: (NfcTag tag) async {
String identifier = '';
try {
final dynamic tagDynamic = tag;
final Map<String, dynamic> tagData = Map<String, dynamic>.from(tagDynamic.data as Map);
if (Platform.isAndroid) {
if (tagData.containsKey('nfca')) {
final nfcaData = tagData['nfca'] as Map<dynamic, dynamic>;
if (nfcaData.containsKey('identifier')) {
final List<int> idBytes = List<int>.from(nfcaData['identifier'] as List);
identifier = idBytes.map((e) => e.toRadixString(16).padLeft(2, '0')).join('');
String identifier = '';
try {
final dynamic tagDynamic = tag;
final Map<String, dynamic> tagData = Map<String, dynamic>.from(tagDynamic.data as Map);
if (Platform.isAndroid) {
if (tagData.containsKey('nfca')) {
final nfcaData = tagData['nfca'] as Map<dynamic, dynamic>;
if (nfcaData.containsKey('identifier')) {
final List<int> idBytes = List<int>.from(nfcaData['identifier'] as List);
identifier = idBytes.map((e) => e.toRadixString(16).padLeft(2, '0')).join('');
}
}
}
} else {
if (tagData.containsKey('mifare')) {
final mifareData = tagData['mifare'] as Map<dynamic, dynamic>;
if (mifareData.containsKey('identifier')) {
final List<int> idBytes = List<int>.from(mifareData['identifier'] as List);
identifier = idBytes.map((e) => e.toRadixString(16).padLeft(2, '0')).join('');
} else {
if (tagData.containsKey('mifare')) {
final mifareData = tagData['mifare'] as Map<dynamic, dynamic>;
if (mifareData.containsKey('identifier')) {
final List<int> idBytes = List<int>.from(mifareData['identifier'] as List);
identifier = idBytes.map((e) => e.toRadixString(16).padLeft(2, '0')).join('');
}
}
}
} catch (e) {
print('Error reading NFC: $e');
}
} catch (e) {
print('Error reading NFC: $e');
}
NfcManager.instance.stopSession();
onRead!(identifier);
},
NfcManager.instance.stopSession();
onRead!(identifier);
},
pollingOptions: {NfcPollingOption.iso14443},
).catchError((err) {
print(err);

@ -1,7 +1,6 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:mobile_scanner/mobile_scanner.dart';
import 'package:qr_code_scanner_plus/qr_code_scanner_plus.dart';
import '../buttons/app_icon_button.dart';
@ -15,10 +14,7 @@ class ScanQr extends StatefulWidget {
class _ScanQrState extends State<ScanQr> {
// Barcode result;
// QRViewController? _controller;
MobileScannerController? controller;
QRViewController? _controller;
bool _scanDone = false;
final GlobalKey qrKey = GlobalKey(debugLabel: 'QR_scanner');
@ -26,34 +22,19 @@ class _ScanQrState extends State<ScanQr> {
// is android, or resume the camera if the platform is iOS.
@override
void initState() {
// TODO: implement initState
super.initState();
controller = MobileScannerController(
detectionSpeed: DetectionSpeed.noDuplicates,
formats: [],
returnImage: false,
torchEnabled: false,
invertImage: false,
autoZoom: true,
);
void reassemble() {
super.reassemble();
if (Platform.isAndroid) {
_controller?.pauseCamera();
} else if (Platform.isIOS) {
_controller?.resumeCamera();
}
}
//
// @override
// void reassemble() {
// super.reassemble();
// if (Platform.isAndroid) {
// controller?.pauseCamera();
// } else if (Platform.isIOS) {
// _controller?.resumeCamera();
// }
// }
@override
void dispose() {
super.dispose();
// _controller?.dispose();
_controller?.dispose();
}
@override
@ -61,15 +42,20 @@ class _ScanQrState extends State<ScanQr> {
return Scaffold(
body: Stack(
children: [
MobileScanner(
tapToFocus: true,
controller: controller,
onDetect: (result) {
if (!_scanDone) {
_scanDone = true;
Navigator.of(context).pop(result.barcodes.first.rawValue);
}
QRView(
key: qrKey,
onQRViewCreated: (QRViewController controller) {
setState(() {
_controller = controller;
});
controller.scannedDataStream.listen((scanData) {
if (!_scanDone) {
_scanDone = true;
Navigator.of(context).pop(scanData.code);
}
});
},
overlay: QrScannerOverlayShape(borderColor: Colors.red, borderRadius: 10, borderLength: 30, borderWidth: 10, cutOutSize: 280),
),
SafeArea(
child: Padding(

@ -217,6 +217,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.19.1"
convert:
dependency: transitive
description:
name: convert
sha256: b30acd5944035672bc15c6b7a8b47d773e41e2f17de064350988c5d02adb1c68
url: "https://pub.dev"
source: hosted
version: "3.1.2"
cross_file:
dependency: transitive
description:
@ -532,6 +540,14 @@ packages:
description: flutter
source: sdk
version: "0.0.0"
flutter_nfc_kit:
dependency: "direct main"
description:
name: flutter_nfc_kit
sha256: a2b324785173930f1b5afac8b190637d612593b994ecfd0a5334b72a841b9298
url: "https://pub.dev"
source: hosted
version: "3.6.2"
flutter_plugin_android_lifecycle:
dependency: transitive
description:
@ -1030,6 +1046,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.0.0"
mobile_scanner:
dependency: "direct main"
description:
name: mobile_scanner
sha256: c92c26bf2231695b6d3477c8dcf435f51e28f87b1745966b1fe4c47a286171ce
url: "https://pub.dev"
source: hosted
version: "7.2.0"
native_toolchain_c:
dependency: transitive
description:
@ -1038,6 +1062,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.17.4"
ndef:
dependency: transitive
description:
name: ndef
sha256: "198ba3798e80cea381648569d84059dbba64cd140079fb7b0d9c3f1e0f5973f3"
url: "https://pub.dev"
source: hosted
version: "0.4.0"
ndef_record:
dependency: transitive
description:
@ -1747,6 +1779,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.4.0"
universal_platform:
dependency: transitive
description:
name: universal_platform
sha256: "64e16458a0ea9b99260ceb5467a214c1f298d647c659af1bff6d3bf82536b1ec"
url: "https://pub.dev"
source: hosted
version: "1.1.0"
url_launcher:
dependency: "direct main"
description:

@ -97,6 +97,8 @@ dependencies:
huawei_location: ^6.16.0+300
geolocator: ^9.0.2
nfc_manager: ^4.1.1
flutter_nfc_kit: ^3.6.2
wifi_iot: ^0.3.19+2
just_audio: ^0.9.46
safe_device: ^1.3.8

Loading…
Cancel
Save