780 lines
30 KiB
Dart
780 lines
30 KiB
Dart
import 'dart:convert';
|
|
import 'dart:io';
|
|
import 'dart:math';
|
|
import 'dart:ui' as ui;
|
|
|
|
import 'package:cloud_firestore/cloud_firestore.dart';
|
|
import 'package:customer/controllers/theme_controller.dart';
|
|
import 'package:customer/models/currency_model.dart';
|
|
import 'package:customer/models/order_model.dart';
|
|
import 'package:customer/models/tax_model.dart';
|
|
import 'package:customer/models/user_model.dart';
|
|
import 'package:customer/models/vendor_model.dart';
|
|
import 'package:customer/models/zone_model.dart';
|
|
import 'package:customer/themes/app_them_data.dart';
|
|
import 'package:customer/utils/preferences.dart';
|
|
import 'package:firebase_storage/firebase_storage.dart';
|
|
import 'package:flutter/foundation.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter/services.dart';
|
|
import 'package:geolocator/geolocator.dart' as geolocator;
|
|
import 'package:geolocator/geolocator.dart';
|
|
import 'package:get/get.dart';
|
|
import 'package:google_maps_flutter/google_maps_flutter.dart';
|
|
import 'package:intl/intl.dart';
|
|
import 'package:mailer/mailer.dart';
|
|
import 'package:mailer/smtp_server.dart';
|
|
import 'package:url_launcher/url_launcher.dart';
|
|
import 'package:uuid/uuid.dart';
|
|
|
|
import '../models/cart_product_model.dart';
|
|
import '../models/coupon_model.dart';
|
|
import '../models/email_template_model.dart';
|
|
import '../models/language_model.dart';
|
|
import '../models/mail_setting.dart';
|
|
import '../models/section_model.dart';
|
|
import '../service/fire_store_utils.dart';
|
|
import '../themes/show_toast_dialog.dart';
|
|
import '../widget/permission_dialog.dart';
|
|
import 'package:http/http.dart' as http;
|
|
|
|
RxList<CartProductModel> cartItem = <CartProductModel>[].obs;
|
|
|
|
class Constant {
|
|
static const userPlaceHolder = "assets/images/user_placeholder.png";
|
|
|
|
static String senderId = '';
|
|
static String jsonNotificationFileURL = '';
|
|
static String appVersion = '';
|
|
static List<TaxModel> taxList = [];
|
|
static String? country = "";
|
|
static String? selectedMapType = "";
|
|
static String? websiteUrl = '';
|
|
static MailSettings? mailSettings;
|
|
static bool isSubscriptionModelApplied = false;
|
|
static CurrencyModel? currencyData;
|
|
static SectionModel? sectionConstantModel;
|
|
static geolocator.Position? currentLocation;
|
|
|
|
static String cabServiceType = "cab-service";
|
|
static String parcelServiceType = "parcel-service";
|
|
|
|
static bool isZoneAvailable = false;
|
|
static ZoneModel? selectedZone;
|
|
static List<ZoneModel> zoneList = [];
|
|
|
|
static List sectionColor = [
|
|
[Color(0xFFFEF8E7), Color(0xFFF7CD59)],
|
|
[Color(0xFFEEEBF9), Color(0xFF8F7CD8)],
|
|
[Color(0xFFFFF8E5), Color(0xFFF7CD59)],
|
|
[Color(0xFFF5E5FF), Color(0xFFCC80FF)],
|
|
[Color(0xFFFAEBEB), Color(0xFFDB7474)],
|
|
[Color(0xFFE5F9FF), Color(0xFF72DEFF)],
|
|
[Color(0xFFEFF5F1), Color(0xFFADCEB7)],
|
|
[Color(0xFFEAFBF1), Color(0xFF85E5AE)],
|
|
[Color(0xFFE7F8FE), Color(0xFF529DB6)],
|
|
[Color(0xFFEEEBF9), Color(0xFF8F7CD8)],
|
|
];
|
|
|
|
static List colorList = [
|
|
Color(0xFFFFBC99),
|
|
const Color(0xFFCABDFF),
|
|
const Color(0xFFB1E5FC),
|
|
const Color(0xFFB5EBCD),
|
|
const Color(0xFFFFD88D),
|
|
const Color(0xFFCBEBA4),
|
|
const Color(0xFFFB9B9B),
|
|
const Color(0xFFF8B0ED),
|
|
const Color(0xFFAFC6FF),
|
|
];
|
|
|
|
static String userRoleDriver = 'driver';
|
|
static String userRoleCustomer = 'customer';
|
|
static String userRoleVendor = 'vendor';
|
|
|
|
static ShippingAddress selectedLocation = ShippingAddress();
|
|
static UserModel? userModel;
|
|
static const globalUrl = "https://Replace_your_domain/";
|
|
|
|
static String mapAPIKey = "";
|
|
static String placeHolderImage = "";
|
|
static String defaultCountryCode = "";
|
|
static String defaultCountry = "";
|
|
|
|
static bool isCashbackActive = false;
|
|
static bool isEnableOTPTripStart = false;
|
|
static bool isEnableOTPTripStartForRental = false;
|
|
static bool isMaintenanceModeForCustomer = false;
|
|
|
|
static String distanceType = "km";
|
|
|
|
static String googlePlayLink = "";
|
|
static String appStoreLink = "";
|
|
static String termsAndConditions = "";
|
|
static String privacyPolicy = "";
|
|
static String supportURL = "";
|
|
static String minimumAmountToDeposit = "0.0";
|
|
static String minimumAmountToWithdrawal = "0.0";
|
|
static bool? walletSetting = true;
|
|
static bool? storyEnable = true;
|
|
static bool? specialDiscountOffer = true;
|
|
|
|
static const String orderPlaced = "Order Placed";
|
|
static const String orderAccepted = "Order Accepted";
|
|
static const String orderRejected = "Order Rejected";
|
|
static const String orderCancelled = "Order Cancelled";
|
|
static const String driverPending = "Driver Pending";
|
|
static const String driverRejected = "Driver Rejected";
|
|
static const String driverAccepted = 'Driver Accepted';
|
|
static const String orderShipped = "Order Shipped";
|
|
static const String orderInTransit = "In Transit";
|
|
static const String orderCompleted = "Order Completed";
|
|
|
|
static const String orderAssigned = "Order Assigned";
|
|
static const String orderOngoing = "Order Ongoing";
|
|
static const String bookingPlaced = "booking_placed";
|
|
|
|
static CurrencyModel? currencyModel;
|
|
static List<VendorModel>? restaurantList = [];
|
|
|
|
static String walletTopup = "wallet_topup";
|
|
static String newVendorSignup = "new_vendor_signup";
|
|
static String payoutRequestStatus = "payout_request_status";
|
|
static String payoutRequest = "payout_request";
|
|
|
|
static String newOrderPlaced = "order_placed";
|
|
static String scheduleOrder = "schedule_order";
|
|
static String dineInPlaced = "dinein_placed";
|
|
static String dineInCanceled = "dinein_canceled";
|
|
static String dineinAccepted = "dinein_accepted";
|
|
static String restaurantRejected = "restaurant_rejected";
|
|
static String driverCompleted = "driver_completed";
|
|
static String restaurantAccepted = "restaurant_accepted";
|
|
static String takeawayCompleted = "takeaway_completed";
|
|
static String newParcelBook = "new_parcel_book";
|
|
static String newOnDemandBook = "new_ondemand_book";
|
|
|
|
// static String selectedMapType = 'osm';
|
|
static String? mapType = "google";
|
|
|
|
static String? we = "google";
|
|
|
|
static bool isEnableAdsFeature = true;
|
|
static bool isSelfDeliveryFeature = false;
|
|
|
|
static double getDoubleVal(dynamic input) {
|
|
if (input == null) return 0.1;
|
|
if (input is int) return input.toDouble();
|
|
if (input is double) return input;
|
|
return 0.1;
|
|
}
|
|
|
|
static bool checkZoneCheck(double latitude, double longLatitude) {
|
|
bool isZoneAvailable = false;
|
|
for (var element in Constant.zoneList) {
|
|
if (Constant.isPointInPolygon(LatLng(latitude, longLatitude), element.area!)) {
|
|
isZoneAvailable = true;
|
|
break;
|
|
} else {
|
|
isZoneAvailable = false;
|
|
}
|
|
}
|
|
return isZoneAvailable;
|
|
}
|
|
|
|
static String? getZoneId(double latitude, double longLatitude) {
|
|
String? zoneId;
|
|
for (var element in Constant.zoneList) {
|
|
if (Constant.isPointInPolygon(LatLng(latitude, longLatitude), element.area!)) {
|
|
zoneId = element.id;
|
|
break;
|
|
}
|
|
}
|
|
return zoneId;
|
|
}
|
|
|
|
static String getReferralCode() {
|
|
var rng = Random();
|
|
return (rng.nextInt(900000) + 100000).toString(); // 6 digit
|
|
}
|
|
|
|
static Future<void> checkPermission({required BuildContext context, required Function() onTap}) async {
|
|
LocationPermission permission = await Geolocator.checkPermission();
|
|
if (permission == LocationPermission.denied) {
|
|
permission = await Geolocator.requestPermission();
|
|
}
|
|
if (permission == LocationPermission.denied) {
|
|
ShowToastDialog.showToast("You have to allow location permission to use your location");
|
|
} else if (permission == LocationPermission.deniedForever) {
|
|
showDialog(
|
|
context: context,
|
|
builder: (BuildContext context) {
|
|
return const PermissionDialog();
|
|
},
|
|
);
|
|
} else {
|
|
onTap();
|
|
}
|
|
}
|
|
|
|
static bool get isRtl {
|
|
final locale = Get.locale ?? Get.deviceLocale ?? const Locale('en');
|
|
return Bidi.isRtlLanguage(locale.languageCode);
|
|
}
|
|
|
|
static bool isExpire(VendorModel venderModel) {
|
|
bool isPlanExpire = false;
|
|
if (venderModel.subscriptionPlan?.id != null) {
|
|
if (venderModel.subscriptionExpiryDate == null) {
|
|
if (venderModel.subscriptionPlan?.expiryDay == '-1') {
|
|
isPlanExpire = false;
|
|
} else {
|
|
isPlanExpire = true;
|
|
}
|
|
} else {
|
|
DateTime expiryDate = venderModel.subscriptionExpiryDate!.toDate();
|
|
isPlanExpire = expiryDate.isBefore(DateTime.now());
|
|
}
|
|
} else {
|
|
isPlanExpire = true;
|
|
}
|
|
return isPlanExpire;
|
|
}
|
|
|
|
static bool isExpireDate({required bool expiryDay, Timestamp? subscriptionExpiryDate}) {
|
|
bool isPlanExpire = false;
|
|
if (expiryDay == true) {
|
|
isPlanExpire = false;
|
|
} else {
|
|
if (subscriptionExpiryDate != null) {
|
|
DateTime expiryDate = subscriptionExpiryDate.toDate();
|
|
isPlanExpire = expiryDate.isBefore(DateTime.now());
|
|
} else {
|
|
isPlanExpire = true;
|
|
}
|
|
}
|
|
|
|
return isPlanExpire;
|
|
}
|
|
|
|
static Future<void> showProgress(String message, bool isDismissible) async {
|
|
ShowToastDialog.showLoader("$message ");
|
|
}
|
|
|
|
static void hideProgress() {
|
|
ShowToastDialog.closeLoader();
|
|
}
|
|
|
|
static String amountShow({required String? amount}) {
|
|
if (currencyModel!.symbolatright == true) {
|
|
return "${double.parse(amount.toString()).toStringAsFixed(currencyModel?.decimal ?? 0)} ${currencyModel!.symbol.toString()}";
|
|
} else {
|
|
return "${currencyModel!.symbol.toString()} ${amount == null || amount.isEmpty ? "0.0" : double.parse(amount.toString()).toStringAsFixed(currencyModel?.decimal ?? 0)}";
|
|
}
|
|
}
|
|
|
|
static Color statusColor({required String? status}) {
|
|
if (status == orderPlaced) {
|
|
return AppThemeData.ecommerce300;
|
|
} else if (status == orderAccepted || status == orderCompleted) {
|
|
return AppThemeData.success400;
|
|
} else if (status == orderRejected) {
|
|
return AppThemeData.danger300;
|
|
} else {
|
|
return AppThemeData.warning300;
|
|
}
|
|
}
|
|
|
|
static Color statusText({required String? status}) {
|
|
if (status == orderPlaced) {
|
|
return AppThemeData.grey50;
|
|
} else if (status == orderAccepted || status == orderCompleted) {
|
|
return AppThemeData.grey50;
|
|
} else if (status == orderRejected) {
|
|
return AppThemeData.grey50;
|
|
} else {
|
|
return AppThemeData.grey900;
|
|
}
|
|
}
|
|
|
|
static String productCommissionPrice(VendorModel vendorModel, String price) {
|
|
String commission = "0";
|
|
if (sectionConstantModel!.adminCommision!.isEnabled == true) {
|
|
if (vendorModel.adminCommission == null) {
|
|
if (sectionConstantModel!.adminCommision!.commissionType!.toLowerCase() == "Percent".toLowerCase() ||
|
|
sectionConstantModel!.adminCommision!.commissionType?.toLowerCase() == "Percentage".toLowerCase()) {
|
|
commission = (double.parse(price) + (double.parse(price) * double.parse(sectionConstantModel!.adminCommision!.amount.toString()) / 100)).toString();
|
|
} else {
|
|
commission = (double.parse(price) + double.parse(sectionConstantModel!.adminCommision!.amount.toString())).toString();
|
|
}
|
|
} else {
|
|
if (vendorModel.adminCommission!.commissionType!.toLowerCase() == "Percent".toLowerCase() || vendorModel.adminCommission!.commissionType?.toLowerCase() == "Percentage".toLowerCase()) {
|
|
commission = (double.parse(price) + (double.parse(price) * double.parse(vendorModel.adminCommission!.amount.toString()) / 100)).toString();
|
|
} else {
|
|
commission = (double.parse(price) + double.parse(vendorModel.adminCommission!.amount.toString())).toString();
|
|
}
|
|
}
|
|
} else {
|
|
commission = price;
|
|
}
|
|
|
|
return commission;
|
|
}
|
|
|
|
static double calculateTax({String? amount, TaxModel? taxModel}) {
|
|
double taxAmount = 0.0;
|
|
if (taxModel != null && taxModel.enable == true) {
|
|
if (taxModel.type == "fix") {
|
|
taxAmount = double.parse(taxModel.tax.toString());
|
|
} else {
|
|
taxAmount = (double.parse(amount.toString()) * double.parse(taxModel.tax!.toString())) / 100;
|
|
}
|
|
}
|
|
return taxAmount;
|
|
}
|
|
|
|
static double calculateDiscount({String? amount, CouponModel? offerModel}) {
|
|
double taxAmount = 0.0;
|
|
if (offerModel != null) {
|
|
if (offerModel.discountType == "Percentage" || offerModel.discountType == "percentage") {
|
|
taxAmount = (double.parse(amount.toString()) * double.parse(offerModel.discount.toString())) / 100;
|
|
} else {
|
|
taxAmount = double.parse(offerModel.discount.toString());
|
|
}
|
|
}
|
|
return taxAmount;
|
|
}
|
|
|
|
static String calculateReview({required String? reviewCount, required String? reviewSum}) {
|
|
if (0 == double.parse(reviewSum.toString()) && 0 == double.parse(reviewSum.toString())) {
|
|
return "0";
|
|
}
|
|
return (double.parse(reviewSum.toString()) / double.parse(reviewCount.toString())).toStringAsFixed(1);
|
|
}
|
|
|
|
static String getUuid() {
|
|
return const Uuid().v4();
|
|
}
|
|
|
|
static Widget loader() {
|
|
return Center(child: CircularProgressIndicator(color: AppThemeData.primary300));
|
|
}
|
|
|
|
static Widget showEmptyView({required String message}) {
|
|
final themeController = Get.find<ThemeController>();
|
|
final isDark = themeController.isDark.value;
|
|
return Center(child: Text(message, style: TextStyle(fontFamily: AppThemeData.fontFamily, fontSize: 18, color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900)));
|
|
}
|
|
|
|
static String maskingString(String documentId, int maskingDigit) {
|
|
String maskedDigits = documentId;
|
|
for (int i = 0; i < documentId.length - maskingDigit; i++) {
|
|
maskedDigits = maskedDigits.replaceFirst(documentId[i], "*");
|
|
}
|
|
return maskedDigits;
|
|
}
|
|
|
|
String? validateRequired(String? value, String type) {
|
|
if (value!.isEmpty) {
|
|
return '$type required';
|
|
}
|
|
return null;
|
|
}
|
|
|
|
String? validateEmail(String? value) {
|
|
String pattern = r'^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$';
|
|
RegExp regExp = RegExp(pattern);
|
|
if (value == null || value.isEmpty) {
|
|
return "Email is Required";
|
|
} else if (!regExp.hasMatch(value)) {
|
|
return "Invalid Email";
|
|
} else {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
static String getDistance({required String lat1, required String lng1, required String lat2, required String lng2}) {
|
|
double distance;
|
|
double distanceInMeters = Geolocator.distanceBetween(double.parse(lat1), double.parse(lng1), double.parse(lat2), double.parse(lng2));
|
|
if (distanceType == "miles") {
|
|
distance = distanceInMeters / 1609;
|
|
} else {
|
|
distance = distanceInMeters / 1000;
|
|
}
|
|
return distance.toStringAsFixed(2);
|
|
}
|
|
|
|
bool hasValidUrl(String? value) {
|
|
String pattern = r'(http|https)://[\w-]+(\.[\w-]+)+([\w.,@?^=%&:/~+#-]*[\w@?^=%&/~+#-])?';
|
|
RegExp regExp = RegExp(pattern);
|
|
if (value == null || value.isEmpty) {
|
|
return false;
|
|
} else if (!regExp.hasMatch(value)) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static Future<String> uploadUserImageToFireStorage(File image, String filePath, String fileName) async {
|
|
Reference upload = FirebaseStorage.instance.ref().child('$filePath/$fileName');
|
|
UploadTask uploadTask = upload.putFile(image);
|
|
var downloadUrl = await (await uploadTask.whenComplete(() {})).ref.getDownloadURL();
|
|
return downloadUrl.toString();
|
|
}
|
|
|
|
static Future<void> makePhoneCall(String phoneNumber) async {
|
|
final Uri launchUri = Uri(scheme: 'tel', path: phoneNumber);
|
|
await launchUrl(launchUri);
|
|
}
|
|
|
|
Future<void> launchURL(Uri url) async {
|
|
if (!await launchUrl(url, mode: LaunchMode.externalApplication)) {
|
|
throw Exception('Could not launch $url');
|
|
}
|
|
}
|
|
|
|
Future<Uint8List> getBytesFromAsset(String path, int width) async {
|
|
ByteData data = await rootBundle.load(path);
|
|
ui.Codec codec = await ui.instantiateImageCodec(data.buffer.asUint8List(), targetWidth: width);
|
|
ui.FrameInfo fi = await codec.getNextFrame();
|
|
return (await fi.image.toByteData(format: ui.ImageByteFormat.png))!.buffer.asUint8List();
|
|
}
|
|
|
|
static Future<TimeOfDay?> selectTime(context) async {
|
|
FocusScope.of(context).requestFocus(FocusNode()); //remove focus
|
|
TimeOfDay? newTime = await showTimePicker(context: context, initialTime: TimeOfDay.now());
|
|
if (newTime != null) {
|
|
return newTime;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
static Future<DateTime?> selectDate(context) async {
|
|
DateTime? pickedDate = await showDatePicker(
|
|
context: context,
|
|
builder: (context, child) {
|
|
return Theme(
|
|
data: Theme.of(context).copyWith(
|
|
colorScheme: ColorScheme.light(
|
|
primary: AppThemeData.primary300, // header background color
|
|
onPrimary: AppThemeData.grey900, // header text color
|
|
onSurface: AppThemeData.grey900, // body text color
|
|
),
|
|
textButtonTheme: TextButtonThemeData(
|
|
style: TextButton.styleFrom(
|
|
foregroundColor: AppThemeData.grey900, // button text color
|
|
),
|
|
),
|
|
),
|
|
child: child!,
|
|
);
|
|
},
|
|
initialDate: DateTime.now(),
|
|
//get today's date
|
|
firstDate: DateTime(2000),
|
|
//DateTime.now() - not to allow to choose before today.
|
|
lastDate: DateTime(2101),
|
|
);
|
|
return pickedDate;
|
|
}
|
|
|
|
static int calculateDifference(DateTime date) {
|
|
DateTime now = DateTime.now();
|
|
return DateTime(date.year, date.month, date.day).difference(DateTime(now.year, now.month, now.day)).inDays;
|
|
}
|
|
|
|
static String timestampToDate(Timestamp timestamp) {
|
|
DateTime dateTime = timestamp.toDate();
|
|
return DateFormat('MMM dd,yyyy').format(dateTime);
|
|
}
|
|
|
|
static String timestampToDateTime(Timestamp timestamp) {
|
|
DateTime dateTime = timestamp.toDate();
|
|
return DateFormat('MMM dd,yyyy hh:mm aa').format(dateTime);
|
|
}
|
|
|
|
static String timestampToDateTime2(Timestamp timestamp) {
|
|
DateTime dateTime = timestamp.toDate();
|
|
return DateFormat('EEE MMM d yyyy').format(dateTime);
|
|
}
|
|
|
|
static String timestampToTime(Timestamp timestamp) {
|
|
DateTime dateTime = timestamp.toDate();
|
|
return DateFormat('hh:mm aa').format(dateTime);
|
|
}
|
|
|
|
static String timestampToDateChat(Timestamp timestamp) {
|
|
DateTime dateTime = timestamp.toDate();
|
|
return DateFormat('dd/MM/yyyy').format(dateTime);
|
|
}
|
|
|
|
static DateTime stringToDate(String openDineTime) {
|
|
return DateFormat('HH:mm').parse(DateFormat('HH:mm').format(DateFormat("hh:mm a").parse((Intl.getCurrentLocale() == "en_US") ? openDineTime : openDineTime.toLowerCase())));
|
|
}
|
|
|
|
static LanguageModel getLanguage() {
|
|
final String user = Preferences.getString(Preferences.languageCodeKey);
|
|
Map<String, dynamic> userMap = jsonDecode(user);
|
|
return LanguageModel.fromJson(userMap);
|
|
}
|
|
|
|
static String orderId({String orderId = ''}) {
|
|
return "#$orderId";
|
|
//return "#${(orderId).substring(orderId.length - 10)}";
|
|
}
|
|
|
|
static bool isPointInPolygon(LatLng point, List<GeoPoint> polygon) {
|
|
int crossings = 0;
|
|
for (int i = 0; i < polygon.length; i++) {
|
|
int next = (i + 1) % polygon.length;
|
|
if (polygon[i].latitude <= point.latitude && polygon[next].latitude > point.latitude || polygon[i].latitude > point.latitude && polygon[next].latitude <= point.latitude) {
|
|
double edgeLong = polygon[next].longitude - polygon[i].longitude;
|
|
double edgeLat = polygon[next].latitude - polygon[i].latitude;
|
|
double interpol = (point.latitude - polygon[i].latitude) / edgeLat;
|
|
if (point.longitude < polygon[i].longitude + interpol * edgeLong) {
|
|
crossings++;
|
|
}
|
|
}
|
|
}
|
|
return (crossings % 2 != 0);
|
|
}
|
|
|
|
static final smtpServer = SmtpServer(
|
|
mailSettings!.host.toString(),
|
|
username: mailSettings!.userName.toString(),
|
|
password: mailSettings!.password.toString(),
|
|
port: 465,
|
|
ignoreBadCertificate: false,
|
|
ssl: true,
|
|
allowInsecure: true,
|
|
);
|
|
|
|
static Future<void> sendMail({String? subject, String? body, bool? isAdmin = false, List<dynamic>? recipients}) async {
|
|
// Create our message.
|
|
if (mailSettings != null) {
|
|
if (isAdmin == true) {
|
|
recipients!.add(mailSettings!.userName.toString());
|
|
}
|
|
final message =
|
|
Message()
|
|
..from = Address(mailSettings!.userName.toString(), mailSettings!.fromName.toString())
|
|
..recipients = recipients!
|
|
..subject = subject
|
|
..text = body
|
|
..html = body;
|
|
|
|
try {
|
|
final sendReport = await send(message, smtpServer);
|
|
print('Message sent: $sendReport');
|
|
} on MailerException catch (e) {
|
|
print(e);
|
|
print('Message not sent.');
|
|
for (var p in e.problems) {
|
|
print('Problem: ${p.code}: ${p.msg}');
|
|
}
|
|
}
|
|
}
|
|
|
|
// var connection = PersistentConnection(smtpServer);
|
|
//
|
|
// // Send the first message
|
|
// await connection.send(message);
|
|
}
|
|
|
|
static Uri createCoordinatesUrl(double latitude, double longitude, [String? label]) {
|
|
Uri uri;
|
|
if (kIsWeb) {
|
|
uri = Uri.https('www.google.com', '/maps/search/', {'api': '1', 'query': '$latitude,$longitude'});
|
|
} else if (Platform.isAndroid) {
|
|
var query = '$latitude,$longitude';
|
|
if (label != null) query += '($label)';
|
|
uri = Uri(scheme: 'geo', host: '0,0', queryParameters: {'q': query});
|
|
} else if (Platform.isIOS) {
|
|
var params = {'ll': '$latitude,$longitude'};
|
|
if (label != null) params['q'] = label;
|
|
uri = Uri.https('maps.apple.com', '/', params);
|
|
} else {
|
|
uri = Uri.https('www.google.com', '/maps/search/', {'api': '1', 'query': '$latitude,$longitude'});
|
|
}
|
|
|
|
return uri;
|
|
}
|
|
|
|
static Future<void> sendOrderEmail({required OrderModel orderModel}) async {
|
|
EmailTemplateModel? emailTemplateModel = await FireStoreUtils.getEmailTemplates(newOrderPlaced);
|
|
if (emailTemplateModel != null) {
|
|
String firstHTML = """
|
|
<table style="width: 100%; border-collapse: collapse; border: 1px solid rgb(0, 0, 0);">
|
|
<thead>
|
|
<tr>
|
|
<th style="text-align: left; border: 1px solid rgb(0, 0, 0);">Product Name<br></th>
|
|
<th style="text-align: left; border: 1px solid rgb(0, 0, 0);">Quantity<br></th>
|
|
<th style="text-align: left; border: 1px solid rgb(0, 0, 0);">Price<br></th>
|
|
<th style="text-align: left; border: 1px solid rgb(0, 0, 0);">Extra Item Price<br></th>
|
|
<th style="text-align: left; border: 1px solid rgb(0, 0, 0);">Total<br></th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
""";
|
|
|
|
String newString = emailTemplateModel.message.toString();
|
|
newString = newString.replaceAll("{username}", "${Constant.userModel!.firstName} ${Constant.userModel!.lastName}");
|
|
newString = newString.replaceAll("{orderid}", orderModel.id.toString());
|
|
newString = newString.replaceAll("{date}", DateFormat('yyyy-MM-dd').format(orderModel.createdAt!.toDate()));
|
|
newString = newString.replaceAll("{address}", orderModel.address!.getFullAddress());
|
|
newString = newString.replaceAll("{paymentmethod}", orderModel.paymentMethod.toString());
|
|
|
|
double deliveryCharge = 0.0;
|
|
double total = 0.0;
|
|
double specialDiscount = 0.0;
|
|
double discount = 0.0;
|
|
double taxAmount = 0.0;
|
|
double tipValue = 0.0;
|
|
String specialLabel = '(${orderModel.specialDiscount!['special_discount_label']}${orderModel.specialDiscount!['specialType'] == "amount" ? currencyModel!.symbol : "%"})';
|
|
List<String> htmlList = [];
|
|
|
|
if (orderModel.deliveryCharge != null) {
|
|
deliveryCharge = double.parse(orderModel.deliveryCharge.toString());
|
|
}
|
|
if (orderModel.tipAmount != null) {
|
|
tipValue = double.parse(orderModel.tipAmount.toString());
|
|
}
|
|
for (var element in orderModel.products!) {
|
|
if (element.extrasPrice != null && element.extrasPrice!.isNotEmpty && double.parse(element.extrasPrice!) != 0.0) {
|
|
total += double.parse(element.quantity.toString()) * double.parse(element.extrasPrice!);
|
|
}
|
|
total += double.parse(element.quantity.toString()) * double.parse(element.price.toString());
|
|
|
|
List<dynamic>? addon = element.extras;
|
|
String extrasDisVal = '';
|
|
for (int i = 0; i < addon!.length; i++) {
|
|
extrasDisVal += '${addon[i].toString().replaceAll("\"", "")} ${(i == addon.length - 1) ? "" : ","}';
|
|
}
|
|
String product = """
|
|
<tr>
|
|
<td style="width: 20%; border-top: 1px solid rgb(0, 0, 0);">${element.name}</td>
|
|
<td style="width: 20%; border: 1px solid rgb(0, 0, 0);" rowspan="2">${element.quantity}</td>
|
|
<td style="width: 20%; border: 1px solid rgb(0, 0, 0);" rowspan="2">${amountShow(amount: element.price.toString())}</td>
|
|
<td style="width: 20%; border: 1px solid rgb(0, 0, 0);" rowspan="2">${amountShow(amount: element.extrasPrice.toString())}</td>
|
|
<td style="width: 20%; border: 1px solid rgb(0, 0, 0);" rowspan="2">${amountShow(amount: ((double.parse(element.quantity.toString()) * double.parse(element.extrasPrice!) + (double.parse(element.quantity.toString()) * double.parse(element.price.toString())))).toString())}</td>
|
|
</tr>
|
|
<tr>
|
|
<td style="width: 20%;">${extrasDisVal.isEmpty ? "" : "Extra Item : $extrasDisVal"}</td>
|
|
</tr>
|
|
""";
|
|
htmlList.add(product);
|
|
}
|
|
|
|
if (orderModel.specialDiscount!.isNotEmpty) {
|
|
specialDiscount = double.parse(orderModel.specialDiscount!['special_discount'].toString());
|
|
}
|
|
|
|
if (orderModel.couponId != null && orderModel.couponId!.isNotEmpty) {
|
|
discount = double.parse(orderModel.discount.toString());
|
|
}
|
|
|
|
List<String> taxHtmlList = [];
|
|
for (var element in taxList) {
|
|
taxAmount = taxAmount + calculateTax(amount: (total - discount - specialDiscount).toString(), taxModel: element);
|
|
String taxHtml =
|
|
"""<span style="font-size: 1rem;">${element.title}: ${amountShow(amount: calculateTax(amount: (total - discount - specialDiscount).toString(), taxModel: element).toString())}${taxList.indexOf(element) == taxList.length - 1 ? "</span>" : "<br></span>"}""";
|
|
taxHtmlList.add(taxHtml);
|
|
}
|
|
|
|
var totalamount =
|
|
orderModel.deliveryCharge == null || orderModel.deliveryCharge!.isEmpty
|
|
? total + taxAmount - discount - specialDiscount
|
|
: total + taxAmount + double.parse(orderModel.deliveryCharge!) + double.parse(orderModel.tipAmount!) - discount - specialDiscount;
|
|
|
|
newString = newString.replaceAll("{subtotal}", amountShow(amount: total.toString()));
|
|
newString = newString.replaceAll("{coupon}", orderModel.couponId.toString());
|
|
newString = newString.replaceAll("{discountamount}", amountShow(amount: orderModel.discount.toString()));
|
|
newString = newString.replaceAll("{specialcoupon}", specialLabel);
|
|
newString = newString.replaceAll("{specialdiscountamount}", amountShow(amount: specialDiscount.toString()));
|
|
newString = newString.replaceAll("{shippingcharge}", amountShow(amount: deliveryCharge.toString()));
|
|
newString = newString.replaceAll("{tipamount}", amountShow(amount: tipValue.toString()));
|
|
newString = newString.replaceAll("{totalAmount}", amountShow(amount: totalamount.toString()));
|
|
|
|
String tableHTML = htmlList.join();
|
|
String lastHTML = "</tbody></table>";
|
|
newString = newString.replaceAll("{productdetails}", firstHTML + tableHTML + lastHTML);
|
|
newString = newString.replaceAll("{taxdetails}", taxHtmlList.join());
|
|
newString = newString.replaceAll("{newwalletbalance}.", amountShow(amount: Constant.userModel!.walletAmount.toString()));
|
|
|
|
String subjectNewString = emailTemplateModel.subject.toString();
|
|
subjectNewString = subjectNewString.replaceAll("{orderid}", orderModel.id.toString());
|
|
await sendMail(subject: subjectNewString, isAdmin: emailTemplateModel.isSendToAdmin, body: newString, recipients: [Constant.userModel!.email]);
|
|
}
|
|
}
|
|
|
|
double calculateDistance(double lat1, double lon1, double lat2, double lon2) {
|
|
const R = 6371; // Earth's radius in km
|
|
final dLat = _degToRad(lat2 - lat1);
|
|
final dLon = _degToRad(lon2 - lon1);
|
|
final a = sin(dLat / 2) * sin(dLat / 2) + cos(_degToRad(lat1)) * cos(_degToRad(lat2)) * sin(dLon / 2) * sin(dLon / 2);
|
|
final c = 2 * atan2(sqrt(a), sqrt(1 - a));
|
|
return R * c;
|
|
}
|
|
|
|
double _degToRad(double deg) => deg * (pi / 180);
|
|
|
|
String getTimeInTheMinutes({required double distance}) {
|
|
double averageSpeed = 40.0;
|
|
double estimatedTime = (distance / averageSpeed) * 60;
|
|
return "${estimatedTime.toStringAsFixed(2)} minutes";
|
|
}
|
|
|
|
/// Calculate tax amount for a single tax model
|
|
static double getTaxValue({required String amount, required TaxModel taxModel}) {
|
|
double taxVal = 0.0;
|
|
if (taxModel.enable == true) {
|
|
if (taxModel.type == "fix") {
|
|
taxVal = double.tryParse(taxModel.tax.toString()) ?? 0.0;
|
|
} else {
|
|
taxVal = (double.tryParse(amount) ?? 0.0) * (double.tryParse(taxModel.tax.toString()) ?? 0.0) / 100;
|
|
}
|
|
}
|
|
return taxVal;
|
|
}
|
|
|
|
Future<Uint8List> getBytesFromUrl(String url, {int width = 100}) async {
|
|
try {
|
|
final http.Response response = await http.get(Uri.parse(url));
|
|
if (response.statusCode != 200) throw Exception("Failed to load image");
|
|
|
|
final Uint8List bytes = response.bodyBytes;
|
|
final ui.Codec codec = await ui.instantiateImageCodec(bytes, targetWidth: width);
|
|
final ui.FrameInfo frameInfo = await codec.getNextFrame();
|
|
|
|
final ByteData? byteData = await frameInfo.image.toByteData(format: ui.ImageByteFormat.png);
|
|
return byteData!.buffer.asUint8List();
|
|
} catch (e) {
|
|
print("⚠️ getBytesFromUrl error: $e — using default cab icon");
|
|
final ByteData data = await rootBundle.load('assets/images/ic_cab.png');
|
|
return data.buffer.asUint8List();
|
|
}
|
|
}
|
|
|
|
// Future<Uint8List> getBytesFromUrl(String url, {int width = 100}) async {
|
|
// final http.Response response = await http.get(Uri.parse(url));
|
|
// if (response.statusCode != 200) {
|
|
// throw Exception("Failed to load image from $url");
|
|
// }
|
|
//
|
|
// final Uint8List bytes = response.bodyBytes;
|
|
//
|
|
// // Decode & resize
|
|
// final ui.Codec codec = await ui.instantiateImageCodec(bytes, targetWidth: width);
|
|
// final ui.FrameInfo frameInfo = await codec.getNextFrame();
|
|
//
|
|
// final ByteData? byteData = await frameInfo.image.toByteData(format: ui.ImageByteFormat.png);
|
|
// return byteData!.buffer.asUint8List();
|
|
// }
|
|
}
|
|
|
|
extension StringExtension on String {
|
|
String capitalizeString() {
|
|
return "${this[0].toUpperCase()}${substring(1).toLowerCase()}";
|
|
}
|
|
}
|