diff --git a/assets/audio/success_tone_android.mp3 b/assets/audio/success_tone_android.mp3 new file mode 100644 index 00000000..b8ad93f9 Binary files /dev/null and b/assets/audio/success_tone_android.mp3 differ diff --git a/assets/audio/success_tone_ios.caf b/assets/audio/success_tone_ios.caf new file mode 100644 index 00000000..f2bcbaf6 Binary files /dev/null and b/assets/audio/success_tone_ios.caf differ diff --git a/lib/helper/utils.dart b/lib/helper/utils.dart new file mode 100644 index 00000000..5cf0ef01 --- /dev/null +++ b/lib/helper/utils.dart @@ -0,0 +1,420 @@ +import 'dart:convert'; +import 'dart:io'; +import 'dart:typed_data'; + +// import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:fluttertoast/fluttertoast.dart'; +import 'package:google_api_availability/google_api_availability.dart'; +// import 'package:mohem_flutter_app/app_state/app_state.dart'; +// import 'package:mohem_flutter_app/classes/colors.dart'; +// import 'package:mohem_flutter_app/config/routes.dart'; +// import 'package:mohem_flutter_app/exceptions/api_exception.dart'; +// import 'package:mohem_flutter_app/extensions/int_extensions.dart'; +// import 'package:mohem_flutter_app/extensions/string_extensions.dart'; +// import 'package:mohem_flutter_app/extensions/widget_extensions.dart'; +// import 'package:mohem_flutter_app/generated/locale_keys.g.dart'; +// import 'package:mohem_flutter_app/widgets/dialogs/confirm_dialog.dart'; +// import 'package:mohem_flutter_app/widgets/loading_dialog.dart'; +import 'package:nfc_manager/nfc_manager.dart'; +import 'package:nfc_manager/platform_tags.dart'; +import 'package:shared_preferences/shared_preferences.dart'; +import 'package:test_sa/new_views/common_widgets/app_lazy_loading.dart'; +import 'package:test_sa/views/widgets/dialogs/confirm_dialog.dart'; + +// ignore_for_file: avoid_annotating_with_dynamic + +class Utils { + static bool _isLoadingVisible = false; + + static bool get isLoading => _isLoadingVisible; + + static void showToast(String message, {bool longDuration = true}) { + Fluttertoast.showToast( + msg: message, + toastLength: longDuration ? Toast.LENGTH_LONG : Toast.LENGTH_SHORT, + gravity: ToastGravity.BOTTOM, + timeInSecForIosWeb: 1, + backgroundColor: Colors.black54, + textColor: Colors.white, + fontSize: 13.0); + } + + static dynamic getNotNullValue(List list, int index) { + try { + return list[index]; + } catch (ex) { + return null; + } + } + + static int stringToHex(String colorCode) { + try { + return int.parse(colorCode.replaceAll("#", "0xff")); + } catch (ex) { + return (0xff000000); + } + } + + static Future delay(int millis) async { + return await Future.delayed(Duration(milliseconds: millis)); + } + + static void showLoading(BuildContext context) { + WidgetsBinding.instance.addPostFrameCallback((_) { + _isLoadingVisible = true; + + // showDialog(context: context, barrierDismissible: false, builder: (context) => const AppLazyLoading()); + + showDialog( + context: context, + barrierColor: Colors.black.withOpacity(0.5), + useRootNavigator: false, + builder: (BuildContext context) => const AppLazyLoading(), + ).then((value) { + _isLoadingVisible = false; + }); + }); + } + + static void hideLoading(BuildContext context) { + if (_isLoadingVisible) { + _isLoadingVisible = false; + Navigator.of(context).pop(); + } + _isLoadingVisible = false; + } + + static Future getStringFromPrefs(String key) async { + SharedPreferences prefs = await SharedPreferences.getInstance(); + return prefs.getString(key) ?? ""; + } + + static Future removeStringFromPrefs(String key) async { + SharedPreferences prefs = await SharedPreferences.getInstance(); + return prefs.remove(key); + } + + static Future saveStringFromPrefs(String key, String value) async { + SharedPreferences prefs = await SharedPreferences.getInstance(); + return await prefs.setString(key, value); + } + + // static void handleException(dynamic exception, cxt, Function(String)? onErrorMessage) { + // String errorMessage; + // if (exception.error.errorType != null && exception.error.errorType == 4) { + // Navigator.pushNamedAndRemoveUntil(cxt, AppRoutes.appUpdateScreen, (_) => false, arguments: exception.error?.errorMessage); + // } else { + // if (exception is APIException) { + // if (exception.message == APIException.UNAUTHORIZED) { + // return; + // } else { + // errorMessage = exception.error?.errorMessage ?? exception.message; + // } + // } else { + // errorMessage = APIException.UNKNOWN; + // } + // if (onErrorMessage != null) { + // onErrorMessage(errorMessage); + // } else { + // if (!AppState().isAuthenticated) { + // showDialog( + // barrierDismissible: false, + // context: cxt, + // builder: (cxt) => ConfirmDialog( + // message: errorMessage, + // onTap: () { + // Navigator.pushNamedAndRemoveUntil(cxt, AppRoutes.login, (Route route) => false); + // }, + // onCloseTap: () {}, + // ), + // ); + // } else { + // if (cxt != null) { + // confirmDialog(cxt, errorMessage); + // } else { + // showToast(errorMessage); + // } + // } + // } + // } + // } + // + // static Future showErrorDialog({required BuildContext context, required VoidCallback onOkTapped, required String message}) async { + // return showDialog( + // context: context, + // builder: (BuildContext context) => ConfirmDialog( + // message: message, + // onTap: onOkTapped, + // ), + // ); + // } + // + static void confirmDialog(cxt, String message, {VoidCallback? onTap}) { + showDialog( + context: cxt, + builder: (BuildContext cxt) => ConfirmDialog( + message: message, + onTap: onTap, + ), + ); + } + // + // static Widget getNoDataWidget(BuildContext context) { + // return Column( + // mainAxisAlignment: MainAxisAlignment.center, + // crossAxisAlignment: CrossAxisAlignment.center, + // children: [ + // SvgPicture.asset('assets/images/not_found.svg', width: 110.0, height: 110.0), + // LocaleKeys.noDataAvailable.tr().toText16().paddingOnly(top: 15), + // ], + // ).center; + // } + // + // static Widget getNoChatWidget(BuildContext context) { + // return Column( + // mainAxisAlignment: MainAxisAlignment.center, + // crossAxisAlignment: CrossAxisAlignment.center, + // children: [ + // SvgPicture.asset('assets/images/not_found.svg', width: 110.0, height: 110.0), + // LocaleKeys.noDataAvailable.tr().toText16().paddingOnly(top: 15), + // ], + // ).center; + // } + // + // static Uint8List getPostBytes(img) { + // try { + // var b64 = img.replaceFirst('data:image/png;base64,', ''); + // if (img != null && Utils.isBase64(b64)) return Utils.dataFromBase64String(b64); + // } catch (e) {} + // return Uint8List.fromList([]); + // } + // + // static String getBase64FromJpeg(img) { + // try { + // var b64 = img.replaceFirst('data:image/jpeg;base64,', ''); + // return b64; + // } catch (e) {} + // return ""; + // } + // + // static bool isBase64(String str) { + // RegExp _base64 = RegExp(r'^(?:[A-Za-z0-9+\/]{4})*(?:[A-Za-z0-9+\/]{2}==|[A-Za-z0-9+\/]{3}=|[A-Za-z0-9+\/]{4})$'); + // return _base64.hasMatch(str); + // } + // + // static Uint8List dataFromBase64String(String base64String) { + // return base64Decode(base64String); + // } + // + // static Widget tableColumnTitle(String? text, {bool showDivider = true, bool alignCenter = false}) { + // text ??= ""; + // return Column( + // crossAxisAlignment: CrossAxisAlignment.start, + // mainAxisSize: MainAxisSize.min, + // children: [ + // 6.height, + // alignCenter ? text.toText12().center : text.toText12(), + // 5.height, + // if (showDivider) + // const Divider( + // height: 1, + // color: Color(0xff2E303A), + // thickness: 1, + // ) + // ], + // ); + // } + // + // static Decoration containerRadius(Color background, double radius) { + // return BoxDecoration( + // color: background, + // border: Border.all( + // width: 1, // + // color: background // <--- border width here + // ), + // borderRadius: BorderRadius.circular(radius), + // ); + // } + // + // static Widget mHeight(double h) { + // return Container( + // height: h, + // ); + // } + // + // static Widget mDivider(Color color) { + // return Divider( + // // width: double.infinity, + // height: 1, + // color: color, + // ); + // } + // + // static Widget tableColumnValue(String text, {bool isCapitable = true, bool alignCenter = false}) { + // return Column( + // crossAxisAlignment: CrossAxisAlignment.start, + // mainAxisSize: MainAxisSize.min, + // children: [ + // 12.height, + // if (alignCenter) + // (isCapitable ? text.toLowerCase().capitalizeFirstofEach : text).toText12(color: MyColors.normalTextColor).center + // else + // (isCapitable ? text.toLowerCase().capitalizeFirstofEach : text).toText12(color: MyColors.normalTextColor), + // 12.height, + // ], + // ); + // } + // + // /// EIT Forms date formats + // + // static String getMonthNamedFormat(DateTime date) { + // /// it will return like "29-Sep-2022" + // return DateFormat('dd-MMM-yyyy', "en_US").format(date); + // } + // + // static String reverseFormatDate(String date) { + // String formattedDate; + // if (date.isNotEmpty) { + // formattedDate = date.replaceAll('/', '-'); + // formattedDate = formattedDate.replaceAll(' 00:00:00', ''); + // } else { + // formattedDate = date; + // } + // return formattedDate; + // } + // + // static String formatStandardDate(String date) { + // String formattedDate; + // if (date.isNotEmpty) { + // formattedDate = date.replaceAll('-', '/'); + // } else { + // formattedDate = date; + // } + // return formattedDate; + // } + // + // static String reverseFormatStandardDate(String date) { + // String formattedDate; + // if (date.isNotEmpty) { + // formattedDate = date.replaceAll('/', '-'); + // } else { + // formattedDate = date; + // } + // return formattedDate; + // } + // + // static String formatDate(String date) { + // String formattedDate; + // + // if (date.isNotEmpty) { + // date = date.substring(0, 10); + // formattedDate = date.replaceAll('-', '/'); + // formattedDate = formattedDate + ' 00:00:00'; + // } else { + // formattedDate = date; + // } + // return formattedDate; + // } + // + // static String formatDateNew(String date) { + // String formattedDate; + // if (date.isNotEmpty) { + // formattedDate = date.split('T')[0]; + // if (!formattedDate.contains("00:00:00")) { + // formattedDate = formattedDate + ' 00:00:00'; + // } + // } else { + // formattedDate = date; + // } + // return formattedDate; + // } + // + // static String formatDateDefault(String date) { + // if (date.isNotEmpty) { + // if (date.toLowerCase().contains("t")) { + // date = date.toLowerCase().split("t")[0]; + // if (!date.contains("00:00:00")) { + // date = date + ' 00:00:00'; + // } + // return date; + // } else { + // if (date.toLowerCase().split("-")[1].length == 3) { + // return DateFormat('dd-MM-yyyy', "en_US").format(DateFormat('dd-MMM-yyyy', "en_US").parseLoose(date)); + // } else { + // return DateFormat('dd-MM-yyyy', "en_US").format(DateFormat('yyyy-MM-dd', "en_US").parseLoose(date)); + // } + // // return DateFormat('yyyy-MM-dd').format(DateFormat('dd-MM-yyyy').parseLoose(date)); + // } + // } else { + // return date; + // } + // } + // + // static Future selectDate(BuildContext context, DateTime selectedDate) async { + // if (!Platform.isIOS) { + // await showCupertinoModalPopup( + // context: context, + // builder: (BuildContext cxt) => Container( + // height: 250, + // color: Colors.white, + // child: CupertinoDatePicker( + // backgroundColor: Colors.white, + // mode: CupertinoDatePickerMode.date, + // onDateTimeChanged: (DateTime value) { + // if (value != null && value != selectedDate) { + // selectedDate = value; + // } + // }, + // initialDateTime: selectedDate, + // ), + // ), + // ); + // } else { + // DateTime? picked = await showDatePicker(context: context, initialDate: selectedDate, initialEntryMode: DatePickerEntryMode.calendarOnly, firstDate: DateTime(2015, 8), lastDate: DateTime(2101)); + // if (picked != null && picked != selectedDate) { + // selectedDate = picked; + // } + // } + // return selectedDate; + // } + + static void readNFc({required Function(String) onRead}) { + NfcManager.instance.startSession(onDiscovered: (NfcTag tag) async { + MifareUltralight f; + if (Platform.isAndroid) { + f = MifareUltralight(tag: tag, identifier: tag.data["nfca"]["identifier"], type: 2, maxTransceiveLength: 252, timeout: 22); + } else { + f = MifareUltralight(tag: tag, identifier: tag.data["mifare"]["identifier"], type: 2, maxTransceiveLength: 252, timeout: 22); + } + String identifier = f.identifier.map((e) => e.toRadixString(16).padLeft(2, '0')).join(''); + NfcManager.instance.stopSession(); + onRead(identifier); + }).catchError((err) { + print(err); + }); + } + + //HUAWEI DECISION MAKING + static Future isGoogleServicesAvailable() async { + GooglePlayServicesAvailability availability = await GoogleApiAvailability.instance.checkGooglePlayServicesAvailability(); + String status = availability.toString().split('.').last; + if (status == "success") { + return true; + } + return false; + } + // + // static bool isDate(String input, String format) { + // try { + // DateTime d = DateFormat(format).parseStrict(input); + // //print(d); + // return true; + // } catch (e) { + // //print(e); + // return false; + // } + // } +} diff --git a/lib/new_views/pages/land_page/land_page.dart b/lib/new_views/pages/land_page/land_page.dart index 3a3a74dd..4b021818 100644 --- a/lib/new_views/pages/land_page/land_page.dart +++ b/lib/new_views/pages/land_page/land_page.dart @@ -15,6 +15,7 @@ import 'package:test_sa/models/enums/user_types.dart'; import 'package:test_sa/new_views/app_style/app_color.dart'; import 'package:test_sa/new_views/common_widgets/app_filled_button.dart'; import 'package:test_sa/new_views/pages/land_page/create_request-type_bottomsheet.dart'; +import 'package:test_sa/new_views/pages/land_page/mark_attendance_widget.dart'; import 'package:test_sa/new_views/pages/land_page/my_request/my_requests_page.dart'; import 'package:test_sa/new_views/pages/land_page/non_hmg_employee_swipe_view.dart'; import 'package:test_sa/new_views/pages/settings_page.dart'; @@ -143,7 +144,7 @@ class _LandPageState extends State { )) : null, drawer: const AppDrawer(), - body: _pages.isEmpty ? NonHmgEmployeeSwipeView() : _pages[currentPageIndex], + body: _pages.isEmpty ? MarkAttendanceWidget() : _pages[currentPageIndex], bottomNavigationBar: _pages.isEmpty ? null : AppBottomNavigationBar( diff --git a/lib/new_views/pages/land_page/mark_attendance_widget.dart b/lib/new_views/pages/land_page/mark_attendance_widget.dart new file mode 100644 index 00000000..77385429 --- /dev/null +++ b/lib/new_views/pages/land_page/mark_attendance_widget.dart @@ -0,0 +1,677 @@ +import 'dart:async'; +import 'dart:io'; + +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:geolocator/geolocator.dart'; +import 'package:huawei_location/huawei_location.dart'; +// import 'package:mohem_flutter_app/widgets/nfc/nfc_reader_sheet.dart'; +// import 'package:mohem_flutter_app/widgets/qr_scanner_dialog.dart'; +import 'package:nfc_manager/nfc_manager.dart'; +import 'package:permission_handler/permission_handler.dart'; +// import 'package:platform_device_id/platform_device_id.dart'; +import 'package:test_sa/dashboard_latest/widgets/app_bar_widget.dart'; +import 'package:test_sa/extensions/text_extensions.dart'; +import 'package:test_sa/extensions/widget_extensions.dart'; +import 'package:test_sa/helper/utils.dart'; +import 'package:test_sa/nfc/nfc_reader_sheet.dart'; +// import 'package:mohem_flutter_app/api/dashboard_api_client.dart'; +// import 'package:mohem_flutter_app/app_state/app_state.dart'; +// import 'package:mohem_flutter_app/classes/colors.dart'; +// import 'package:mohem_flutter_app/classes/utils.dart'; +// import 'package:mohem_flutter_app/extensions/string_extensions.dart'; +// import 'package:mohem_flutter_app/extensions/widget_extensions.dart'; +// import 'package:mohem_flutter_app/generated/locale_keys.g.dart'; +// import 'package:mohem_flutter_app/models/generic_response_model.dart'; +// import 'package:mohem_flutter_app/models/privilege_list_model.dart'; +// import 'package:mohem_flutter_app/provider/dashboard_provider_model.dart'; +// import 'package:mohem_flutter_app/ui/dialogs/success_dialog.dart'; +// import 'package:mohem_flutter_app/widgets/dialogs/confirm_dialog.dart'; +// import 'package:mohem_flutter_app/widgets/dialogs/dialogs.dart';im + +import 'package:test_sa/utilities/Location.dart' as location; +import 'package:test_sa/views/widgets/dialogs/confirm_dialog.dart'; +import 'package:test_sa/views/widgets/dialogs/success_dialog.dart'; +import 'package:test_sa/views/widgets/qr_scanner_dialog.dart'; +import 'package:wifi_iot/wifi_iot.dart'; + +import '../../app_style/app_color.dart'; + +class MarkAttendanceWidget extends StatefulWidget { + // DashboardProviderModel model; + double topPadding; + bool isFromDashboard; + + MarkAttendanceWidget( {Key? key, this.topPadding = 0, this.isFromDashboard = false}) : super(key: key); + // todo MarkAttendanceWidget(this.model, {Key? key, this.topPadding = 0, this.isFromDashboard = false}) : super(key: key); + + @override + _MarkAttendanceWidgetState createState() { + return _MarkAttendanceWidgetState(); + } +} + +class _MarkAttendanceWidgetState extends State { + bool isNfcEnabled = false, isNfcLocationEnabled = false, isQrEnabled = false, isQrLocationEnabled = false, isWifiEnabled = false, isWifiLocationEnabled = false; + + int _locationUpdateCbId = 0; + + @override + void initState() { + super.initState(); + checkAttendanceAvailability(); + } + + void checkAttendanceAvailability() async { + bool isAvailable = await NfcManager.instance.isAvailable(); + // setState(() { + // AppState().privilegeListModel!.forEach((PrivilegeListModel element) { + // if (element.serviceName == "enableNFC") { + // if (isAvailable) if (element.previlege ?? false) isNfcEnabled = true; + // } else if (element.serviceName == "enableQR") { + // if (element.previlege ?? false) isQrEnabled = true; + // } else if (element.serviceName == "enableWIFI") { + // if (element.previlege ?? false) isWifiEnabled = true; + // } else if (element.serviceName!.trim() == "enableLocationNFC") { + // if (element.previlege ?? false) isNfcLocationEnabled = true; + // } else if (element.serviceName == "enableLocationQR") { + // if (element.previlege ?? false) isQrLocationEnabled = true; + // } else if (element.serviceName == "enableLocationWIFI") { + // if (element.previlege ?? false) isWifiLocationEnabled = true; + // } + // }); + // }); + } + + void checkHuaweiLocationPermission(String attendanceType) async { + // Permission_Handler permissionHandler = PermissionHandler(); + location.Location.isEnabled((bool isEnabled) async { + if (isEnabled) { + location.Location.havePermission((bool permission) async { + if (permission) { + 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); + }, + ), + ); + } + } + }); + } 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); + // }, + // ), + // ); + // } + // } + } + + Future requestPermissions() async { + var result = await [ + Permission.location, + ].request(); + return (result[Permission.location] == PermissionStatus.granted || result[Permission.locationAlways] == PermissionStatus.granted); + } + + @override + void dispose() { + super.dispose(); + // Stop Session + NfcManager.instance.stopSession(); + } + + @override + Widget build(BuildContext context) { + return Container( + padding: EdgeInsets.only(left: 21, right: 21, bottom: 21, top: widget.topPadding), + decoration: const BoxDecoration(borderRadius: BorderRadius.only(topLeft: Radius.circular(25), topRight: Radius.circular(25)), color: Colors.white), + width: double.infinity, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + // LocaleKeys.markAttendance.tr().toSectionHeading(), + // LocaleKeys.selectMethodOfAttendance.tr().toText11(color: const Color(0xff535353)), + GridView( + physics: const NeverScrollableScrollPhysics(), + shrinkWrap: true, + padding: const EdgeInsets.only(bottom: 0, top: 21), + gridDelegate: + SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: (MediaQuery.of(context).size.width < 550) ? 3 : 5, childAspectRatio: 1 / 1, crossAxisSpacing: 8, mainAxisSpacing: 8), + children: [ + attendanceMethod("NFC", Icons.nfc, isNfcEnabled, () { + // if (AppState().getIsHuawei) { + if (false) { + checkHuaweiLocationPermission("NFC"); + } else { + location.Location.isEnabled((bool isEnabled) { + if (isEnabled) { + location.Location.havePermission((bool permission) { + if (permission) { + Utils.showLoading(context); + location.Location.getCurrentLocation( + (Position position, bool isMocked) { + if (isMocked) { + Utils.hideLoading(context); + markFakeAttendance("NFC", position.latitude.toString() ?? "", position.longitude.toString() ?? ""); + } else { + Utils.hideLoading(context); + //todo performNfcAttendance(widget.model, lat: position.latitude.toString() ?? "", lng: position.longitude.toString() ?? ""); + } + }, + () { + Utils.hideLoading(context); + Utils.confirmDialog(context, "Unable to determine your location, Please make sure that your location services are turned on & working."); + }, + context, + ); + } else { + showDialog( + context: context, + builder: (BuildContext cxt) => ConfirmDialog( + message: "You need to give location permission to mark attendance", + onTap: () async { + Navigator.pop(context); + await Geolocator.openAppSettings(); + }, + ), + ); + } + }); + } 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 (isWifiEnabled) //todo + attendanceMethod("Wifi", Icons.wifi, isWifiEnabled, () { + // if (AppState().getIsHuawei) { + if (false) { + checkHuaweiLocationPermission("WIFI"); + } else { + location.Location.isEnabled((bool isEnabled) { + if (isEnabled) { + location.Location.havePermission((bool permission) { + if (permission) { + Utils.showLoading(context); + location.Location.getCurrentLocation( + (Position position, bool isMocked) { + if (isMocked) { + Utils.hideLoading(context); + markFakeAttendance("WIFI", position.latitude.toString() ?? "", position.longitude.toString() ?? ""); + } else { + Utils.hideLoading(context); + //todo performWifiAttendance(widget.model, lat: position.latitude.toString() ?? "", lng: position.longitude.toString() ?? ""); + } + }, + () { + Utils.hideLoading(context); + Utils.confirmDialog(context, "Unable to determine your location, Please make sure that your location services are turned on & working."); + }, + context, + ); + } else { + showDialog( + context: context, + builder: (BuildContext cxt) => ConfirmDialog( + message: "You need to give location permission to mark attendance", + onTap: () async { + Navigator.pop(context); + await Geolocator.openAppSettings(); + }, + ), + ); + } + }); + } 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 (isQrEnabled) //todo + attendanceMethod("QR", Icons.qr_code_2, isQrEnabled, () async { + // if (AppState().getIsHuawei) { + if (false) { + checkHuaweiLocationPermission("QR"); + } else { + location.Location.isEnabled((bool isEnabled) { + if (isEnabled) { + location.Location.havePermission((bool permission) { + if (permission) { + Utils.showLoading(context); + location.Location.getCurrentLocation( + (Position position, bool isMocked) { + if (isMocked) { + Utils.hideLoading(context); + markFakeAttendance("QR", position.latitude.toString() ?? "", position.longitude.toString() ?? ""); + } else { + Utils.hideLoading(context); + //todo performQrCodeAttendance(widget.model, lat: position.latitude.toString() ?? "", lng: position.longitude.toString() ?? ""); + } + }, + () { + Utils.hideLoading(context); + Utils.confirmDialog(context, "Unable to determine your location, Please make sure that your location services are turned on & working."); + }, + context, + ); + } else { + showDialog( + context: context, + builder: (BuildContext cxt) => ConfirmDialog( + message: "You need to give location permission to mark attendance", + onTap: () async { + Navigator.pop(context); + await Geolocator.openAppSettings(); + }, + ), + ); + } + }); + } 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(); + }, + ), + ); + } + }); + } + }), + ], + ) + ], + ), + ); + } + + void getHuaweiCurrentLocation(String attendanceType) async { + try { + Utils.showLoading(context); + FusedLocationProviderClient locationService = FusedLocationProviderClient()..initFusedLocationService(); + LocationRequest locationRequest = LocationRequest(); + locationRequest.priority = LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY; + locationRequest.interval = 500; + List locationRequestList = [locationRequest]; + LocationSettingsRequest locationSettingsRequest = LocationSettingsRequest(requests: locationRequestList); + + late StreamSubscription _streamSubscription; + int requestCode = (await (locationService.requestLocationUpdates(locationRequest)))!; + + _streamSubscription = locationService.onLocationData!.listen( + (Location location) async { + Utils.hideLoading(context); + await locationService.removeLocationUpdates(requestCode); + if (attendanceType == "QR") { + // todo performQrCodeAttendance(widget.model, lat: location.latitude.toString() ?? "", lng: location.longitude.toString() ?? ""); + } + if (attendanceType == "WIFI") { + // todo performWifiAttendance(widget.model, lat: location.latitude.toString() ?? "", lng: location.longitude.toString() ?? ""); + } + if (attendanceType == "NFC") { + //todo performNfcAttendance(widget.model, lat: location.latitude.toString() ?? "", lng: location.longitude.toString() ?? ""); + } + requestCode = 0; + }, + ); + + // locationService.checkLocationSettings(locationSettingsRequest).then((settings) async { + // await locationService.getLastLocation().then((value) { + // if (value.latitude == null || value.longitude == null) { + // showDialog( + // context: context, + // builder: (BuildContext cxt) => ConfirmDialog( + // message: "Unable to get your location, Please check your location settings & try again.", + // onTap: () { + // Navigator.pop(context); + // }, + // ), + // ); + // } else { + // if (attendanceType == "QR") { + // performQrCodeAttendance(widget.model, lat: value.latitude.toString() ?? "", lng: value.longitude.toString() ?? ""); + // } + // if (attendanceType == "WIFI") { + // performWifiAttendance(widget.model, lat: value.latitude.toString() ?? "", lng: value.longitude.toString() ?? ""); + // } + // if (attendanceType == "NFC") { + // performNfcAttendance(widget.model, lat: value.latitude.toString() ?? "", lng: value.longitude.toString() ?? ""); + // } + // } + // }).catchError((error) { + // print("HUAWEI LOCATION getLastLocation ERROR!!!!!"); + // print(error); + // }); + // }).catchError((error) { + // print("HUAWEI LOCATION checkLocationSettings ERROR!!!!!"); + // print(error); + // if (error.code == "LOCATION_SETTINGS_NOT_AVAILABLE") { + // // Location service not enabled. + // } + // }); + } catch (error) { + print("HUAWEI LOCATION ERROR!!!!!"); + print(error); + Utils.hideLoading(context); + // Utils.handleException(error, context, null); + } + } + // + // Future performNfcAttendance(DashboardProviderModel model, {String lat = "0", String lng = "0"}) async { + // if (Platform.isIOS) { + // Utils.readNFc(onRead: (String nfcId) async { + // Utils.showLoading(context); + // try { + // GenericResponseModel? g = await DashboardApiClient().markAttendance(pointType: 2, nfcValue: nfcId, isGpsRequired: isNfcLocationEnabled, lat: lat, long: lng); + // if (g?.messageStatus != 1) { + // Utils.hideLoading(context); + // showDialog( + // context: context, + // builder: (BuildContext cxt) => ConfirmDialog( + // message: g?.errorEndUserMessage ?? "Unexpected error occurred", + // onTap: () { + // Navigator.pop(context); + // }, + // ), + // ); + // } else { + // bool status = await model.fetchAttendanceTracking(context); + // if (Platform.isIOS) await Future.delayed(const Duration(seconds: 3)); + // Utils.hideLoading(context); + // showMDialog( + // context, + // backgroundColor: Colors.transparent, + // isDismissable: true, + // child: SuccessDialog(widget.isFromDashboard), + // ); + // } + // } catch (ex) { + // Utils.hideLoading(context); + // Utils.handleException(ex, context, null); + // } + // }); + // } else { + // showNfcReader(context, onNcfScan: (String? nfcId) async { + // Utils.showLoading(context); + // try { + // GenericResponseModel? g = await DashboardApiClient().markAttendance(pointType: 2, nfcValue: nfcId ?? "", isGpsRequired: isNfcLocationEnabled, lat: lat, long: lng); + // if (g?.messageStatus != 1) { + // Utils.hideLoading(context); + // showDialog( + // context: context, + // builder: (BuildContext cxt) => ConfirmDialog( + // message: g?.errorEndUserMessage ?? "Unexpected error occurred", + // onTap: () { + // Navigator.pop(context); + // }, + // ), + // ); + // } else { + // bool status = await model.fetchAttendanceTracking(context); + // Utils.hideLoading(context); + // showMDialog( + // context, + // backgroundColor: Colors.transparent, + // isDismissable: false, + // child: SuccessDialog(widget.isFromDashboard), + // ); + // } + // } catch (ex) { + // print(ex); + // Utils.hideLoading(context); + // // Utils.handleException(ex, context, (String msg) { + // // Utils.confirmDialog(context, msg); + // // }); + // } + // }); + // } + // } + + void showMDialog(context, {Widget? child, Color? backgroundColor, bool isDismissable = true, bool isBusniessCard = false}) async { + return showDialog( + context: context, + barrierDismissible: isDismissable, + builder: (context) { + return Dialog( + shape: isBusniessCard + ? const RoundedRectangleBorder( + borderRadius: BorderRadius.all( + Radius.circular(15.0), + ), + ) + : null, + backgroundColor: backgroundColor, + child: child, + ); + }, + ); + } + // + // Future checkSession() async { + // try { + // Utils.showLoading(context); + // await DashboardApiClient().getOpenMissingSwipes(); + // Utils.hideLoading(context); + // return true; + // } catch (ex) { + // Utils.hideLoading(context); + // Utils.handleException(ex, context, null); + // return false; + // } + // } + + // Future performWifiAttendance(DashboardProviderModel model, {String lat = "0", String lng = "0"}) async { + // if (Platform.isAndroid) { + // if (!(await checkSession())) { + // return; + // } + // } + // Utils.showLoading(context); + // bool isConnected = await WiFiForIoTPlugin.connect(AppState().getMohemmWifiSSID ?? "", + // password: AppState().getMohemmWifiPassword ?? "", joinOnce: Platform.isIOS ? false : true, security: NetworkSecurity.WPA, withInternet: false); + // + // if (Platform.isIOS) { + // if (await WiFiForIoTPlugin.getSSID() == AppState().getMohemmWifiSSID) { + // isConnected = true; + // } else { + // isConnected = false; + // } + // } + // + // if (isConnected && AppState().isAuthenticated) { + // await WiFiForIoTPlugin.forceWifiUsage(true); + // await Future.delayed(const Duration(seconds: 6)); + // try { + // GenericResponseModel? g = await DashboardApiClient().markAttendance(pointType: 3, nfcValue: "", isGpsRequired: isWifiLocationEnabled, lat: lat, long: lng); + // bool status = await model.fetchAttendanceTracking(context); + // Utils.hideLoading(context); + // await closeWifiRequest(); + // if (g?.messageStatus == 2) { + // showDialog( + // barrierDismissible: true, + // context: context, + // builder: (cxt) => ConfirmDialog( + // message: g?.errorEndUserMessage ?? "", + // onTap: () { + // Navigator.pop(context); + // }, + // onCloseTap: () {}, + // ), + // ); + // } else { + // showMDialog( + // context, + // backgroundColor: Colors.transparent, + // isDismissable: false, + // child: SuccessDialog(widget.isFromDashboard), + // ); + // } + // } catch (ex) { + // await closeWifiRequest(); + // Utils.hideLoading(context); + // Utils.handleException(ex, context, null); + // } + // } else { + // if (AppState().isAuthenticated) { + // Utils.hideLoading(context); + // Utils.confirmDialog(context, "LocaleKeys.comeNearHMGWifi.tr()"); + // } else { + // await closeWifiRequest(); + // } + // } + // } + + Future closeWifiRequest() async { + if (Platform.isAndroid) { + await WiFiForIoTPlugin.forceWifiUsage(false); + } + return await WiFiForIoTPlugin.disconnect(); + } + + // Future performQrCodeAttendance(DashboardProviderModel model, {String lat = "0", String lng = "0"}) async { + // var qrCodeValue = await Navigator.of(context).push( + // MaterialPageRoute( + // builder: (BuildContext context) => QrScannerDialog(), + // ), + // ); + // if (qrCodeValue != null) { + // Utils.showLoading(context); + // try { + // GenericResponseModel? g = await DashboardApiClient().markAttendance(pointType: 1, isGpsRequired: isQrLocationEnabled, lat: lat, long: lng, QRValue: qrCodeValue); + // bool status = await model.fetchAttendanceTracking(context); + // Utils.hideLoading(context); + // if (g?.messageStatus == 2) { + // showDialog( + // barrierDismissible: true, + // context: context, + // builder: (cxt) => ConfirmDialog( + // message: g?.errorEndUserMessage ?? "", + // onTap: () { + // Navigator.pop(context); + // }, + // onCloseTap: () {}, + // ), + // ); + // } else { + // showMDialog( + // context, + // backgroundColor: Colors.transparent, + // isDismissable: true, + // child: SuccessDialog(widget.isFromDashboard), + // ); + // } + // } catch (ex) { + // print(ex); + // Utils.hideLoading(context); + // Utils.handleException(ex, context, null); + // } + // } + // } + + void markFakeAttendance(String sourceName, String lat, String long) async { + Utils.showLoading(context); + try { + // await DashboardApiClient().markFakeLocation(sourceName: sourceName, lat: lat, long: long); + Utils.hideLoading(context); + Utils.confirmDialog(context, "LocaleKeys.fakeLocation.tr()"); + } catch (ex) { + print(ex); + Utils.hideLoading(context); + //Utils.handleException(ex, context, null); + } + } + + Widget attendanceMethod(String title, IconData iconData, bool isEnabled, VoidCallback onPress) => Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(15), + color: isEnabled ? null : Colors.grey.withOpacity(.5), + gradient: isEnabled + ? const LinearGradient( + transform: GradientRotation(.64), + begin: Alignment.topRight, + end: Alignment.bottomLeft, + colors: [ + //MyColors.gradiantEndColor, + // MyColors.gradiantStartColor, + ], + ) + : null, + ), + clipBehavior: Clip.antiAlias, + padding: const EdgeInsets.only(left: 10, right: 10, top: 14, bottom: 14), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // SvgPicture.asset(image, color: Colors.white, alignment: Alignment.topLeft).expanded, + Icon(iconData, color: isEnabled ? AppColor.black35 : Colors.grey), + title.heading6(context), + // title.toText17(isBold: true, color: Colors.white), + ], + ), + ).onPress( + () { + if (!isEnabled) return; + onPress(); + }, + ); +} diff --git a/lib/nfc/nfc_reader_sheet.dart b/lib/nfc/nfc_reader_sheet.dart new file mode 100644 index 00000000..b7b97a77 --- /dev/null +++ b/lib/nfc/nfc_reader_sheet.dart @@ -0,0 +1,199 @@ +import 'dart:async'; +import 'dart:io'; + +import 'package:flutter/material.dart'; +import 'package:nfc_manager/nfc_manager.dart'; +import 'package:nfc_manager/platform_tags.dart'; + +void showNfcReader(BuildContext context, {required Function(String? nfcId) onNcfScan}) { + showModalBottomSheet( + context: context, + enableDrag: false, + isDismissible: false, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.only(topLeft: Radius.circular(12), topRight: Radius.circular(12)), + ), + backgroundColor: Colors.white, + builder: (context) { + return NfcLayout( + onNcfScan: onNcfScan, + ); + }, + ); +} + +class NfcLayout extends StatefulWidget { + Function(String? nfcId) onNcfScan; + + NfcLayout({required this.onNcfScan}); + + @override + _NfcLayoutState createState() => _NfcLayoutState(); +} + +class _NfcLayoutState extends State { + bool _reading = false; + Widget? mainWidget; + String? nfcId; + + @override + void initState() { + super.initState(); + + NfcManager.instance.startSession(onDiscovered: (NfcTag tag) async { + var f; + if (Platform.isAndroid) { + f = MifareUltralight(tag: tag, identifier: tag.data["nfca"]["identifier"], type: 2, maxTransceiveLength: 252, timeout: 22); + } else { + f = MifareUltralight(tag: tag, identifier: tag.data["mifare"]["identifier"], type: 2, maxTransceiveLength: 252, timeout: 22); + } + String identifier = f.identifier.map((e) => e.toRadixString(16).padLeft(2, '0')).join(''); + nfcId = identifier; + + setState(() { + _reading = true; + mainWidget = doneNfc(); + }); + + Future.delayed(const Duration(seconds: 1), () { + NfcManager.instance.stopSession(); + Navigator.pop(context); + // if (Platform.isAndroid) { + // Navigator.pop(context); + // } else { + // Navigator.pop(context); + // Navigator.pop(context); + // } + widget.onNcfScan(nfcId); + }); + }).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( + key: ValueKey(1), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + SizedBox( + height: 30, + ), + Text( + "Ready To Scan", + style: TextStyle( + fontWeight: FontWeight.bold, + fontSize: 24, + ), + ), + SizedBox( + height: 30, + ), + Image.asset( + "assets/icons/nfc/ic_nfc.png", + height: MediaQuery.of(context).size.width / 3, + ), + SizedBox( + height: 30, + ), + Text( + "Approach an NFC Tag", + style: TextStyle( + fontSize: 18, + ), + ), + 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: Text("CANCEL"), + ), + ), + SizedBox( + height: 30, + ), + ], + ), + ); + } + + Widget doneNfc() { + return Container( + key: ValueKey(2), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + SizedBox( + height: 30, + ), + Text( + "Successfully Scanned", + style: TextStyle( + fontWeight: FontWeight.bold, + fontSize: 24, + ), + ), + SizedBox( + height: 30, + ), + Image.asset( + "assets/icons/nfc/ic_done.png", + height: MediaQuery.of(context).size.width / 3, + ), + SizedBox( + height: 30, + ), + Text( + "Approach an NFC Tag", + style: TextStyle( + fontSize: 18, + ), + ), + 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"), + ), + ), + SizedBox( + height: 30, + ), + ], + ), + ); + } +} diff --git a/lib/views/widgets/dialogs/confirm_dialog.dart b/lib/views/widgets/dialogs/confirm_dialog.dart new file mode 100644 index 00000000..83b05faa --- /dev/null +++ b/lib/views/widgets/dialogs/confirm_dialog.dart @@ -0,0 +1,60 @@ +import 'package:flutter/material.dart'; +import 'package:test_sa/extensions/int_extensions.dart'; +import 'package:test_sa/extensions/text_extensions.dart'; +import 'package:test_sa/extensions/widget_extensions.dart'; +import 'package:test_sa/new_views/common_widgets/app_filled_button.dart'; + +class ConfirmDialog extends StatelessWidget { + final String? title; + final String message; + final String? okTitle; + final VoidCallback? onTap; + final VoidCallback? onCloseTap; + + const ConfirmDialog({Key? key, this.title, required this.message, this.okTitle, this.onTap, this.onCloseTap}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Dialog( + backgroundColor: Colors.white, + shape: const RoundedRectangleBorder(), + insetPadding: const EdgeInsets.only(left: 21, right: 21), + child: Padding( + padding: const EdgeInsets.only(left: 20, right: 20, top: 18, bottom: 28), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + child: Text( + title ?? "Confirm", + style: const TextStyle(fontSize: 24, fontWeight: FontWeight.w600, color: Colors.black87, height: 35 / 24, letterSpacing: -0.96), + ).paddingOnly(top: 16), + ), + IconButton( + padding: EdgeInsets.zero, + icon: const Icon(Icons.close), + color: Colors.black87, + constraints: const BoxConstraints(), + onPressed: () => onCloseTap ?? Navigator.pop(context), + // onPressed: () => Navigator.pop(context), + ) + ], + ), + message.bodyText(context), + 28.height, + AppFilledButton( + label: okTitle ?? "OK", + onPressed: onTap ?? () => Navigator.pop(context), + textColor: Colors.white, + //color: Ap.green, + ), + ], + ), + ), + ); + } +} diff --git a/lib/views/widgets/dialogs/success_dialog.dart b/lib/views/widgets/dialogs/success_dialog.dart new file mode 100644 index 00000000..65c6ec3e --- /dev/null +++ b/lib/views/widgets/dialogs/success_dialog.dart @@ -0,0 +1,75 @@ +import 'dart:io'; + +import 'package:flutter/material.dart'; +import 'package:just_audio/just_audio.dart'; +import 'package:lottie/lottie.dart'; + +class SuccessDialog extends StatefulWidget { + bool isFromDashboard; + + SuccessDialog(this.isFromDashboard); + + @override + State createState() => _SuccessDialogState(); +} + +class _SuccessDialogState extends State with TickerProviderStateMixin { + late AnimationController _controller; + + @override + void initState() { + _controller = AnimationController(vsync: this); + + super.initState(); + } + + Future playSuccessSound() async { + AudioPlayer player = AudioPlayer(); + String audioAsset = ""; + if (Platform.isAndroid) { + audioAsset = "assets/audio/success_tone_android.mp3"; + } else { + audioAsset = "assets/audio/success_tone_ios.caf"; + } + await player.setAsset(audioAsset); + await player.load(); + player.play(); + } + + @override + Widget build(BuildContext context) { + double size = MediaQuery.of(context).size.width / 1.8; + return Column( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + mainAxisSize: MainAxisSize.min, + children: [ + Container( + width: size, + height: size, + child: Card( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(25.0), + ), + child: Lottie.asset( + 'assets/lottie/lt_success.json', + repeat: false, + reverse: false, + controller: _controller, + frameRate: FrameRate(60.0), + onLoaded: (LottieComposition v) async { + await playSuccessSound(); + _controller + ..duration = v.duration + ..forward().whenComplete(() async { + Navigator.pop(context); + if (widget.isFromDashboard) Navigator.pop(context); + }); + }, + ), + ), + ), + ], + ); + } +} diff --git a/lib/views/widgets/qr_scanner_dialog.dart b/lib/views/widgets/qr_scanner_dialog.dart new file mode 100644 index 00000000..224a4da7 --- /dev/null +++ b/lib/views/widgets/qr_scanner_dialog.dart @@ -0,0 +1,79 @@ + +import 'package:flutter/material.dart'; + +import 'package:qr_code_scanner/qr_code_scanner.dart'; +import 'package:test_sa/new_views/common_widgets/app_filled_button.dart'; + +class QrScannerDialog extends StatefulWidget { + @override + State createState() => _QrScannerDialogState(); +} + +class _QrScannerDialogState extends State { + final GlobalKey qrKey = GlobalKey(debugLabel: 'QR'); + Barcode? result; + QRViewController? controller; + bool isPicked = false; + + @override + Widget build(BuildContext context) { + return Scaffold( + body: Container( + width: double.infinity, + height: double.infinity, + color: Colors.white, + child: Column( + children: [ + Expanded( + flex: 1, + child: QRView( + key: qrKey, + onQRViewCreated: _onQRViewCreated, + ), + ), + // Expanded( + // flex: 1, + // child: Center( + // child: (result != null) + // ? Text( + // 'Barcode Type: ${result!.format} Data: ${result!.code}') + // : Text('Scan a code'), + // ), + // ), + Padding( + padding: const EdgeInsets.all(12.0), + child: AppFilledButton( + label: "LocaleKeys.cancel.tr()", + onPressed: () { + Navigator.pop(context); + }, + ), + ), + ], + ), + ), + ); + } + + void _onQRViewCreated(QRViewController controller) { + this.controller = controller; + + controller.scannedDataStream.listen((scanData) { + setState(() { + result = scanData; + if (!isPicked) { + isPicked = true; + Navigator.pop(context, result!.code); + } + }); + }); + controller.pauseCamera(); + controller.resumeCamera(); + } + + @override + void dispose() { + controller?.dispose(); + super.dispose(); + } +} diff --git a/pubspec.lock b/pubspec.lock index fbb9dc5e..bbc24af6 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -41,6 +41,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.11.0" + audio_session: + dependency: transitive + description: + name: audio_session + sha256: b2a26ba8b7efa1790d6460e82971fde3e398cfbe2295df9dea22f3499d2c12a7 + url: "https://pub.dev" + source: hosted + version: "0.1.23" audioplayers: dependency: "direct main" description: @@ -813,6 +821,30 @@ packages: url: "https://pub.dev" source: hosted version: "4.9.0" + just_audio: + dependency: "direct main" + description: + name: just_audio + sha256: a49e7120b95600bd357f37a2bb04cd1e88252f7cdea8f3368803779b925b1049 + url: "https://pub.dev" + source: hosted + version: "0.9.42" + just_audio_platform_interface: + dependency: transitive + description: + name: just_audio_platform_interface + sha256: "0243828cce503c8366cc2090cefb2b3c871aa8ed2f520670d76fd47aa1ab2790" + url: "https://pub.dev" + source: hosted + version: "4.3.0" + just_audio_web: + dependency: transitive + description: + name: just_audio_web + sha256: "9a98035b8b24b40749507687520ec5ab404e291d2b0937823ff45d92cb18d448" + url: "https://pub.dev" + source: hosted + version: "0.4.13" leak_tracker: dependency: transitive description: @@ -1642,6 +1674,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.1.0" + wifi_iot: + dependency: "direct main" + description: + name: wifi_iot + sha256: "4a3301f71663a908bb50ffe6d9dc9e8b8383fab48368accf978409bde96e9aca" + url: "https://pub.dev" + source: hosted + version: "0.3.19+1" win32: dependency: "direct overridden" description: diff --git a/pubspec.yaml b/pubspec.yaml index d143c266..19596cc3 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -90,6 +90,8 @@ dependencies: huawei_location: ^6.11.0+301 geolocator: ^9.0.2 nfc_manager: ^3.2.0 + wifi_iot: ^0.3.19+1 + just_audio: ^0.9.30 local_auth_darwin: any dev_dependencies: @@ -120,6 +122,7 @@ flutter: uses-material-design: true assets: - assets/ + - assets/audio/ - assets/images/ - assets/lottie/ - assets/subtitles/