diff --git a/assets/fonts/digital-counter-7.regular.ttf b/assets/fonts/digital-counter-7.regular.ttf new file mode 100644 index 0000000..81775ee Binary files /dev/null and b/assets/fonts/digital-counter-7.regular.ttf differ diff --git a/lib/constant/box_name.dart b/lib/constant/box_name.dart index 0ba8277..4f35a06 100644 --- a/lib/constant/box_name.dart +++ b/lib/constant/box_name.dart @@ -10,4 +10,8 @@ class BoxName { static const String email = "email"; static const String tokens = "tokens"; static const String tokenFCM = "tokenFCM"; + static const String cardNumber = "cardNumber"; + static const String cardHolderName = "cardHolderName"; + static const String expiryDate = "expiryDate"; + static const String cvvCode = "cvvCode"; } diff --git a/lib/controller/auth/login_controller.dart b/lib/controller/auth/login_controller.dart index b317cd5..c147549 100644 --- a/lib/controller/auth/login_controller.dart +++ b/lib/controller/auth/login_controller.dart @@ -7,6 +7,7 @@ import 'package:get/get.dart'; import 'package:ride/constant/box_name.dart'; import 'package:ride/constant/links.dart'; import 'package:ride/controller/functions/crud.dart'; +import 'package:ride/controller/functions/secure_storage.dart'; import 'package:ride/main.dart'; import 'package:ride/views/auth/verify_email_page.dart'; import 'package:ride/views/home/map_page.dart'; @@ -20,7 +21,7 @@ class LoginController extends GetxController { TextEditingController passwordController = TextEditingController(); bool isAgreeTerms = false; bool isloading = false; - final FlutterSecureStorage _storage = FlutterSecureStorage(); + final FlutterSecureStorage _storage = const FlutterSecureStorage(); void changeAgreeTerm() { isAgreeTerms = !isAgreeTerms; @@ -33,15 +34,6 @@ class LoginController extends GetxController { update(); } - void saveData(String key, value) async { - await _storage.write(key: key, value: value); - } - - Future readData(String boxName) async { - final String? value = await _storage.read(key: boxName); - return value; - } - void login() async { isloading = true; update(); @@ -65,7 +57,8 @@ class LoginController extends GetxController { box.write(BoxName.email, jsonDecoeded['data'][0]['email']); box.write(BoxName.name, jsonDecoeded['data'][0]['first_name']); box.write(BoxName.phone, jsonDecoeded['data'][0]['phone']); - saveData(BoxName.passwoerd, passwordController.text); + SecureStorage() + .saveData(BoxName.passwoerd, passwordController.text); Get.offAll(() => MapPage()); isloading = false; update(); diff --git a/lib/controller/functions/digit_obsecur_formate.dart b/lib/controller/functions/digit_obsecur_formate.dart new file mode 100644 index 0000000..b5972e4 --- /dev/null +++ b/lib/controller/functions/digit_obsecur_formate.dart @@ -0,0 +1,42 @@ +import 'package:flutter/services.dart'; + +class DigitObscuringFormatter extends TextInputFormatter { + @override + TextEditingValue formatEditUpdate( + TextEditingValue oldValue, TextEditingValue newValue) { + final maskedText = maskDigits(newValue.text); + return newValue.copyWith( + text: maskedText, + selection: updateCursorPosition(maskedText, newValue.selection)); + } + + String maskDigits(String text) { + final totalDigits = text.length; + final visibleDigits = 4; + final hiddenDigits = totalDigits - visibleDigits * 2; + + final firstVisibleDigits = text.substring(0, visibleDigits); + final lastVisibleDigits = text.substring(totalDigits - visibleDigits); + + final maskedDigits = List.filled(hiddenDigits, '*').join(); + + return '$firstVisibleDigits$maskedDigits$lastVisibleDigits'; + } + + TextSelection updateCursorPosition( + String maskedText, TextSelection currentSelection) { + final cursorPosition = currentSelection.baseOffset; + final cursorOffset = + currentSelection.extentOffset - currentSelection.baseOffset; + final totalDigits = maskedText.length; + const visibleDigits = 4; + final hiddenDigits = totalDigits - visibleDigits * 2; + + final updatedPosition = cursorPosition <= visibleDigits + ? cursorPosition + : hiddenDigits + visibleDigits + (cursorPosition - visibleDigits); + + return TextSelection.collapsed( + offset: updatedPosition, affinity: currentSelection.affinity); + } +} diff --git a/lib/controller/functions/scan_id_card.dart b/lib/controller/functions/scan_id_card.dart new file mode 100644 index 0000000..3c7cb96 --- /dev/null +++ b/lib/controller/functions/scan_id_card.dart @@ -0,0 +1,25 @@ +import 'package:credit_card_scanner/credit_card_scanner.dart'; +import 'package:get/get.dart'; + +class ScanIdCard extends GetxController { + CardDetails? _cardDetails; + CardScanOptions scanOptions = const CardScanOptions( + scanCardHolderName: true, +// enableDebugLogs: true, + validCardsToScanBeforeFinishingScan: 5, + possibleCardHolderNamePositions: [ + CardHolderNameScanPosition.aboveCardNumber, + ], + ); + + Future scanCard() async { + final CardDetails? cardDetails = + await CardScanner.scanCard(scanOptions: scanOptions); + if (cardDetails == null) { + return; + } + + _cardDetails = cardDetails; + update(); + } +} diff --git a/lib/controller/functions/secure_storage.dart b/lib/controller/functions/secure_storage.dart new file mode 100644 index 0000000..364dced --- /dev/null +++ b/lib/controller/functions/secure_storage.dart @@ -0,0 +1,14 @@ +import 'package:flutter_secure_storage/flutter_secure_storage.dart'; + +class SecureStorage { + final FlutterSecureStorage _storage = const FlutterSecureStorage(); + + void saveData(String key, value) async { + await _storage.write(key: key, value: value); + } + + Future readData(String boxName) async { + final String? value = await _storage.read(key: boxName); + return value; + } +} diff --git a/lib/controller/home/map_page_controller.dart b/lib/controller/home/map_page_controller.dart index 8cbb9c4..4a63a97 100644 --- a/lib/controller/home/map_page_controller.dart +++ b/lib/controller/home/map_page_controller.dart @@ -48,10 +48,11 @@ class MapController extends GetxController { bool isMainBottomMenuMap = true; double heightButtomSheetShown = 300; double cashConfirmPageShown = 250; - double paymentPageShown = 380; + double paymentPageShown = Get.height * .6; late final LatLng southwest; late final LatLng northeast; List carLocations = []; + // final mainBottomMenuMap = GlobalKey(); void changeButtomSheetShown() { isButtomSheetShown = !isButtomSheetShown; @@ -341,9 +342,9 @@ class MapController extends GetxController { // Animate the camera to the adjusted bounds if (distanceOfDestnation <= 10) { - mapController!.animateCamera(CameraUpdate.newLatLngZoom(mylocation, 14)); - } else if (distanceOfDestnation > 10 && distanceOfDestnation < 16) { mapController!.animateCamera(CameraUpdate.newLatLngZoom(mylocation, 12)); + } else if (distanceOfDestnation > 10 && distanceOfDestnation < 16) { + mapController!.animateCamera(CameraUpdate.newLatLngZoom(mylocation, 11)); } else if (distanceOfDestnation > 16 && distanceOfDestnation < 30) { mapController!.animateCamera(CameraUpdate.newLatLngZoom(mylocation, 10)); } else if (distanceOfDestnation > 30 && distanceOfDestnation < 100) { @@ -376,18 +377,18 @@ class MapController extends GetxController { return distance; } - late double totaME; - late double tax; - late double totalPassenger; - late double totalDriver; - late double averageDuration; - late double costDuration; - late double cost; - late double distance; - late double duration; + late double totaME = 0; + late double tax = 0; + late double totalPassenger = 0; + late double totalDriver = 0; + late double averageDuration = 0; + late double costDuration = 0; + late double cost = 0; + late double distance = 0; + late double duration = 2; DateTime currentTime = DateTime.now(); late Duration durationToAdd; - late DateTime newTime; + late DateTime newTime = DateTime.now(); void bottomSheet() { if (data.isNotEmpty) { String distanceText = data[0]['distance']['text']; @@ -395,12 +396,25 @@ class MapController extends GetxController { distance = getDistanceFromText(distanceText); duration = getDistanceFromText(durationText); durationToAdd = Duration(minutes: duration.toInt()); + DateTime currentTime = DateTime.now(); newTime = currentTime.add(durationToAdd); - - if (distanceText.contains('km')) { - cost = distance * 0.21; + update(); + if (currentTime.hour >= 21) { + if (distanceText.contains('km')) { + cost = distance * 0.23; + update(); + } else { + cost = distance * 0.23 / 1000; + update(); + } } else { - cost = distance * 0.21 / 1000; + if (distanceText.contains('km')) { + cost = distance * 0.21; + update(); + } else { + cost = distance * 0.21 / 1000; + update(); + } } averageDuration = duration / distance; costDuration = duration * averageDuration * 0.016; @@ -410,12 +424,15 @@ class MapController extends GetxController { totaME = totalPassenger - totalDriver - tax; if (totalPassenger < 1) { totalPassenger = 1; + update(); if (totalDriver < .5) { totalDriver = .85; totaME = .11; + update(); } else { totalDriver = .95; totaME = .05; + update(); } } } diff --git a/lib/controller/home/payment/payment_controller.dart b/lib/controller/home/payment/payment_controller.dart new file mode 100644 index 0000000..a340bb6 --- /dev/null +++ b/lib/controller/home/payment/payment_controller.dart @@ -0,0 +1,88 @@ +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; + +import '../../../constant/box_name.dart'; +import '../../functions/digit_obsecur_formate.dart'; +import '../../functions/secure_storage.dart'; + +class CreditCardController extends GetxController { + final GlobalKey formKey = GlobalKey(); + final TextEditingController cardNumberController = TextEditingController(); + final TextEditingController cardHolderNameController = + TextEditingController(); + final TextEditingController expiryDateController = TextEditingController(); + final TextEditingController cvvCodeController = TextEditingController(); + openPayment() async { + String? cardNumber = await SecureStorage().readData(BoxName.cardNumber); + String? cardHolderName = + await SecureStorage().readData(BoxName.cardHolderName); + String? expiryDate = await SecureStorage().readData(BoxName.expiryDate); + String? cvvCode = await SecureStorage().readData(BoxName.cvvCode); + + print('cardNumber: $cardNumber'); + print('cardHolderName: $cardHolderName'); + print('expiryDate: $expiryDate'); + print('cvvCode: $cvvCode'); + + if (cvvCode != null && cvvCode.isNotEmpty) { + final maskedCardNumber = DigitObscuringFormatter() + .formatEditUpdate( + TextEditingValue.empty, + TextEditingValue(text: cardNumber ?? ''), + ) + .text; + + print('maskedCardNumber: $maskedCardNumber'); + + cardNumberController.text = maskedCardNumber; + cardHolderNameController.text = cardHolderName ?? ''; + expiryDateController.text = expiryDate ?? ''; + cvvCodeController.text = cvvCode; + } + } + + @override + void onInit() async { + super.onInit(); + String? cardNumber = await SecureStorage().readData(BoxName.cardNumber); + String? cardHolderName = + await SecureStorage().readData(BoxName.cardHolderName); + String? expiryDate = await SecureStorage().readData(BoxName.expiryDate); + String? cvvCode = await SecureStorage().readData(BoxName.cvvCode); + + print('cardNumber: $cardNumber'); + print('cardHolderName: $cardHolderName'); + print('expiryDate: $expiryDate'); + print('cvvCode: $cvvCode'); + + if (cvvCode != null && cvvCode.isNotEmpty) { + final maskedCardNumber = DigitObscuringFormatter() + .formatEditUpdate( + TextEditingValue.empty, + TextEditingValue(text: cardNumber ?? ''), + ) + .text; + + print('maskedCardNumber: $maskedCardNumber'); + + cardNumberController.text = maskedCardNumber; + cardHolderNameController.text = cardHolderName ?? ''; + expiryDateController.text = expiryDate ?? ''; + cvvCodeController.text = cvvCode; + } + } +} + +class CreditCardModel { + String cardNumber; + String cardHolderName; + String expiryDate; + String cvvCode; + + CreditCardModel({ + required this.cardNumber, + required this.cardHolderName, + required this.expiryDate, + required this.cvvCode, + }); +} diff --git a/lib/main.dart b/lib/main.dart index e42aa52..0823ce9 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -24,7 +24,7 @@ Future backgroundMessageHandler(RemoteMessage message) async { await Firebase.initializeApp(); print('===========back===${message.notification?.title}'); - if (message.notification!.title == 'reef') { + if (message.notification!.title == 'ride') { // SendGpsNow().getSiteNotification(); } } diff --git a/lib/views/home/map_widget.dart/cash_confirm_bottom_page.dart b/lib/views/home/map_widget.dart/cash_confirm_bottom_page.dart index 489ad70..72837b7 100644 --- a/lib/views/home/map_widget.dart/cash_confirm_bottom_page.dart +++ b/lib/views/home/map_widget.dart/cash_confirm_bottom_page.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:ride/constant/style.dart'; +import 'package:ride/controller/home/payment/payment_controller.dart'; import 'package:ride/views/widgets/elevated_btn.dart'; import '../../../constant/colors.dart'; @@ -20,7 +21,9 @@ class CashConfirmPageShown extends StatelessWidget { left: 5, child: AnimatedContainer( duration: const Duration(milliseconds: 400), - height: controller.cashConfirmPageShown, + height: controller.isCashConfirmPageShown + ? controller.cashConfirmPageShown + : 0, decoration: BoxDecoration( color: AppColor.secondaryColor, borderRadius: BorderRadius.circular(15)), @@ -110,10 +113,11 @@ class CashConfirmPageShown extends StatelessWidget { indent: 1, ), MyElevatedButton( - title: 'Add Payment Method', - onPressed: () => - controller.changePaymentMethodPageShown(), - ) + title: 'Add Payment Method', + onPressed: () { + controller.changePaymentMethodPageShown(); + CreditCardController().openPayment(); + }) ], ), ), diff --git a/lib/views/home/map_widget.dart/payment_method.page.dart b/lib/views/home/map_widget.dart/payment_method.page.dart index 490d2a0..039bd74 100644 --- a/lib/views/home/map_widget.dart/payment_method.page.dart +++ b/lib/views/home/map_widget.dart/payment_method.page.dart @@ -1,9 +1,14 @@ import 'package:flutter/material.dart'; + import 'package:get/get.dart'; +import 'package:ride/constant/box_name.dart'; +import 'package:ride/controller/functions/secure_storage.dart'; +import 'package:ride/controller/home/payment/payment_controller.dart'; import 'package:ride/views/widgets/elevated_btn.dart'; import '../../../constant/colors.dart'; import '../../../constant/style.dart'; +import '../../../controller/functions/digit_obsecur_formate.dart'; import '../../../controller/home/map_page_controller.dart'; class PaymentMethodPage extends StatelessWidget { @@ -20,7 +25,9 @@ class PaymentMethodPage extends StatelessWidget { left: 5, child: AnimatedContainer( duration: const Duration(milliseconds: 400), - height: controller.paymentPageShown, + height: controller.isPaymentMethodPageShown + ? controller.paymentPageShown + : 0, decoration: BoxDecoration( color: AppColor.secondaryColor, borderRadius: BorderRadius.circular(15)), @@ -43,41 +50,57 @@ class PaymentMethodPage extends StatelessWidget { ), ], ), - Text( - 'Add Card'.tr, - style: AppStyle.title, + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + 'Add Card'.tr, + style: AppStyle.title, + ), + // GetBuilder( + // builder: (controller) => IconButton( + // onPressed: () { + // // controller.scanCard(); + // // Get.defaultDialog(content: OptionConfigureWidget( + // // initialOptions: scanOptions, + // // onScanOptionChanged: (newOptions) => + // // scanOptions = newOptions, + // // ), + // // ); + // }, + // icon: const Icon(Icons.contact_emergency_sharp), + // ), + // ) + ], ), const SizedBox( height: 10, ), - Container( - height: Get.height * .3, - decoration: const BoxDecoration( - color: AppColor.secondaryColor, - borderRadius: BorderRadius.all(Radius.circular(15)), - boxShadow: [ - BoxShadow( - spreadRadius: 3, - offset: Offset(3, 3), - blurRadius: 3, - color: AppColor.redColor), - BoxShadow( - offset: Offset(-3, -3), - blurRadius: 3, - spreadRadius: 3, - color: AppColor.redColor) - ]), - ), + const CreditCardWidget(), const Spacer(), - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - MyElevatedButton( - title: 'Add Credit Card', - onPressed: () {}, - ), - ], - ) + GetBuilder( + builder: (controller) => Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + MyElevatedButton( + title: 'Add Credit Card', + onPressed: () async { + SecureStorage().saveData( + BoxName.cardNumber, + controller.cardNumberController.text); + SecureStorage().saveData( + BoxName.cardHolderName, + controller + .cardHolderNameController.text); + SecureStorage().saveData(BoxName.cvvCode, + controller.cvvCodeController.text); + SecureStorage().saveData( + BoxName.expiryDate, + controller.expiryDateController.text); + }, + ), + ], + )) ], ), ), @@ -85,3 +108,228 @@ class PaymentMethodPage extends StatelessWidget { )); } } + +class CreditCardWidget extends StatelessWidget { + const CreditCardWidget({ + super.key, + }); + + @override + Widget build(BuildContext context) { + Get.put(CreditCardController()); + return GetBuilder( + builder: (controller) => Container( + height: Get.height * .3, + decoration: const BoxDecoration( + color: AppColor.secondaryColor, + borderRadius: BorderRadius.all(Radius.circular(15)), + boxShadow: [ + BoxShadow( + spreadRadius: 3, + offset: Offset(3, 3), + blurRadius: 3, + color: AppColor.redColor), + BoxShadow( + offset: Offset(-3, -3), + blurRadius: 3, + spreadRadius: 3, + color: AppColor.redColor), + ], + ), + child: Form( + key: controller.formKey, + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Column( + children: [ + SizedBox( + child: Row( + children: [ + getCardIcon(controller.cardNumberController + .text), // Dynamic credit card icon + SizedBox( + width: Get.width * .1, + ), + SizedBox( + width: Get.width * .6, + height: 70, + child: TextFormField( + maxLength: 16, + keyboardType: TextInputType.number, + controller: controller.cardNumberController, + style: const TextStyle( + fontFamily: 'digital-counter-7', + fontWeight: FontWeight.bold), + decoration: const InputDecoration( + helperStyle: TextStyle( + fontFamily: 'digital-counter-7'), + labelText: 'Card Number', + ), + inputFormatters: [DigitObscuringFormatter()], + validator: (value) { + if (value!.isEmpty || value.length != 16) { + return 'Please enter a valid 16-digit card number'; + } + return null; + }, + ), + ), + ], + ), + ), + Row( + children: [ + const Icon(Icons.person), + SizedBox( + width: Get.width * .1, + ), + SizedBox( + width: Get.width * .6, + child: SizedBox( + height: 50, + child: TextFormField( + style: AppStyle.title, + keyboardType: TextInputType.text, + // maxLength: 16, + controller: controller.cardHolderNameController, + decoration: const InputDecoration( + labelText: 'Cardholder Name'), + validator: (value) { + if (value!.isEmpty) { + return 'Please enter the cardholder name'; + } + return null; + }, + ), + ), + ) + ], + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + SizedBox( + width: Get.width * .4, + child: Row( + children: [ + const Icon(Icons.date_range_outlined), + SizedBox( + width: Get.width * .1, + ), + SizedBox( + width: Get.width * .2, + child: SizedBox( + height: 60, + child: TextFormField( + keyboardType: TextInputType.datetime, + controller: + controller.expiryDateController, + decoration: const InputDecoration( + labelText: 'Expiry Date'), + validator: (value) { + if (value!.isEmpty) { + return 'Please enter the expiry date'; + } + return null; + }, + ), + ), + ) + ], + ), + ), + SizedBox( + width: Get.width * .4, + child: Row( + children: [ + const Icon(Icons.security), + SizedBox( + width: Get.width * .021, + ), + SizedBox( + width: Get.width * .2, + child: SizedBox( + height: 60, + child: TextFormField( + obscureText: true, + keyboardType: TextInputType.number, + style: const TextStyle( + fontFamily: 'digital-counter-7'), + maxLength: 3, + controller: controller.cvvCodeController, + decoration: const InputDecoration( + labelText: 'CVV Code'), + validator: (value) { + if (value!.isEmpty && + value.length != 3) { + return 'Please enter the CVV code'; + } + return null; + }, + ), + ), + ) + ], + ), + ), + ], + ), + // MyElevatedButton( + // title: 'Save'.tr, + // onPressed: () { + // if (controller.formKey.currentState!.validate()) { + // final creditCard = CreditCardModel( + // cardNumber: controller.cardNumberController.text, + // cardHolderName: + // controller.cardHolderNameController.text, + // expiryDate: controller.expiryDateController.text, + // cvvCode: controller.cvvCodeController.text, + // ); + // // Process the credit card details + // // You can use GetX to handle the logic here + // } + // }, + // ), + ], + ), + )))); + } + + Widget getCardIcon(String cardNumber) { + String cardType = detectCardType( + cardNumber); // Function to detect card type based on the first digit + + IconData iconData; + Color iconColor; + + switch (cardType) { + case 'Visa': + iconData = Icons.credit_card_rounded; + iconColor = Colors.blue; // Change color for Visa cards + break; + case 'Mastercard': + iconData = Icons.credit_card_rounded; + iconColor = Colors.red; // Change color for Mastercard cards + break; + default: + iconData = Icons.credit_card_rounded; + iconColor = Colors.black; // Default color for other card types + break; + } + + return Icon( + iconData, + color: iconColor, + ); + } + + String detectCardType(String cardNumber) { + if (cardNumber.startsWith('4')) { + return 'Visa'; + } else if (cardNumber.startsWith('5')) { + return 'Mastercard'; + } else { + return 'Other'; + } + } +} diff --git a/pubspec.lock b/pubspec.lock index ff53945..f212ac9 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -81,6 +81,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.1.1" + credit_card_scanner: + dependency: "direct main" + description: + name: credit_card_scanner + sha256: "9bbfcac698f01bb32cbb2abfc6a9efa7fdea006e5a982cb4b8dc69fa0e8c1d19" + url: "https://pub.dev" + source: hosted + version: "1.0.5" crypto: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index d07b1de..01b65fa 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -54,6 +54,7 @@ dependencies: custom_searchable_dropdown: ^2.1.1 animated_text_kit: ^4.2.2 flutter_secure_storage: ^8.0.0 + credit_card_scanner: ^1.0.5 dev_dependencies: flutter_test: @@ -81,24 +82,13 @@ flutter: assets: - assets/images/ - assets/lottie/ - # - images/a_dot_ham.jpeg + - assets/fonts/ - # An image asset can refer to one or more resolution-specific "variants", see - # https://flutter.dev/assets-and-images/#resolution-aware - - # For details regarding adding assets from package dependencies, see - # https://flutter.dev/assets-and-images/#from-packages - - # To add custom fonts to your application, add a fonts section here, - # in this "flutter" section. Each entry in this list should have a - # "family" key with the font family name, and a "fonts" key with a - # list giving the asset and other descriptors for the font. For - # example: - # fonts: - # - family: Schyler - # fonts: - # - asset: fonts/Schyler-Regular.ttf - # - asset: fonts/Schyler-Italic.ttf + fonts: + - family: digital-counter-7 + fonts: + - asset: assets/fonts/digital-counter-7.regular.ttf + # style: italic # - family: Trajan Pro # fonts: