import 'dart:convert'; import 'dart:io'; import 'dart:typed_data'; import 'dart:ui' as ui; import 'package:cached_network_image/cached_network_image.dart'; import 'package:dio/dio.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:image_picker/image_picker.dart'; import 'package:path/path.dart'; import 'package:stripe_payment/stripe_payment.dart'; import '../constants.dart'; import '../events/eventbus.dart'; import '../events/events.dart'; import '../generated/l10n.dart'; import '../models/comment.dart'; import '../models/order.dart'; import '../models/payment_platform.dart'; import '../models/stripe_payment_method.dart'; import '../models/user.dart'; import '../routes.dart'; import '../store/actions.dart'; import '../store/store.dart'; import '../widgets/general/stripe_pay.dart'; import 'http_util.dart'; import 'utils.dart'; typedef void OnGotFile(int imageId, String filePath); class Util { static final Util _util = Util._internal(); factory Util() { return _util; } Util._internal(); static bool notificationTapped = false; static Widget showImage(String imageUrl, {double? width, double? height, BoxFit? fit, Widget Function(BuildContext, String, dynamic)? errorWidget}) { if (imageUrl.isNotEmpty && imageUrl.startsWith('https:')) { return CachedNetworkImage( imageUrl: imageUrl, width: width, height: width, fit: fit, placeholder: (context, url) => Utils.imageLoadingIndicator(width: width ?? 30, height: height ?? 30), errorWidget: errorWidget != null ? errorWidget : (context, url, error) { return Image.asset( 'assets/images/not_found.png', width: width, height: height, fit: fit, ); }, ); } else if (imageUrl.isNotEmpty) { return Image.file( File(imageUrl), width: width, height: height, fit: fit, errorBuilder: (BuildContext context, Object object, StackTrace? stackTrace) { return Image.asset( 'assets/images/not_found.png', width: width, height: height, fit: fit, ); }, ); } else { return Image.asset( 'assets/images/not_found.png', width: width, height: height, fit: fit, ); } } static void openWebUrl(BuildContext context, String link) { Routes.router.navigateTo(context, '/webview/$link'); } AlertDialog getPicture(BuildContext context, User user, {int commentId = -1, int orderId = 0}) { return AlertDialog( title: Text( S.of(context).get_picture, ), content: Container( height: 130.0, child: Column( children: [ Container( margin: EdgeInsets.only(bottom: 12.0), child: Text( S.of(context).get_picture_from, style: TextStyle( fontSize: 15.0, color: Colors.black26, ), ), ), Container( child: TextButton( child: Row( mainAxisAlignment: MainAxisAlignment.start, children: [ Icon( Icons.photo_library, ), Container( margin: EdgeInsets.only(left: 5.0), child: Text( S.of(context).gallery, ), ), ], ), onPressed: () { getPictureFromGallery(context, user, commentId: commentId, orderId: orderId); }, ), ), Container( child: TextButton( child: Row( mainAxisAlignment: MainAxisAlignment.start, children: [ Icon( Icons.photo_camera, ), Container( margin: EdgeInsets.only(left: 5.0), child: Text( S.of(context).camera, ), ), ], ), onPressed: () { getPictureFromCamera(context, user, commentId: commentId, orderId: orderId); }, ), ) ], ), ), ); } void getPictureFromGallery(BuildContext context, User user, {int commentId = -1, int orderId = 0}) async { final picker = ImagePicker(); var image = await picker.pickImage(source: ImageSource.gallery); Navigator.of(context).pop(); uploadPicture(context, File(image!.path), user, commentId: commentId, orderId: orderId); } void getPictureFromCamera(BuildContext context, User user, {int commentId = -1, int orderId = 0}) async { final picker = ImagePicker(); var image = await picker.pickImage(source: ImageSource.camera); Navigator.of(context).pop(); uploadPicture(context, File(image!.path), user, commentId: commentId, orderId: orderId); } void uploadPicture(BuildContext context, File image, User user, {int commentId = -1, int orderId = 0}) async { if (await image.exists()) { print('image: ${image.path}'); String imageName = basename(image.path); MultipartFile imageFile = await MultipartFile.fromFile(image.path, filename: imageName); Map formMap = { 'id': user.id, 'file': imageFile, }; if (commentId >= 0) { formMap = { 'contact_id': user.id, 'comment_id': commentId, 'order_id': orderId, 'file': imageFile, }; HttpUtil.httpPost( 'v1/upload-comment-image', (response) { eventBus.fire(new OnCommentUpdatedEvent(Comment.fromJson(response.data))); }, isFormData: true, body: formMap, sendProgress: (int sent, int total) { if (sent == total) { eventBus.fire(OnUploadCommentImageProgressEvent(false, 0.0)); } else { eventBus.fire(OnUploadCommentImageProgressEvent(true, sent / total)); } }, ).catchError((error) { Utils.showMessageDialog(context, error); }); } else { HttpUtil.httpPost( 'v1/upload-avatar', (response) { store.dispatch(new UpdateCurrentUser(User.fromJson(response.data))); eventBus.fire(new OnCurrentUserUpdated()); }, isFormData: true, body: formMap, sendProgress: (int sent, int total) { if (sent == total) { eventBus.fire(OnProgressEvent(false, 0.0)); } else { eventBus.fire(OnProgressEvent(true, sent / total)); } }, ).catchError((error) { Utils.showMessageDialog(context, error); }); } } } AlertDialog getPicture2(BuildContext context, int imageId, OnGotFile onGotFile) { return AlertDialog( title: Text( S.of(context).get_picture, ), content: Container( height: 130.0, child: Column( children: [ Container( margin: EdgeInsets.only(bottom: 12.0), child: Text( S.of(context).get_picture_from, style: TextStyle( fontSize: 15.0, color: Colors.black26, ), ), ), Container( child: TextButton( child: Row( mainAxisAlignment: MainAxisAlignment.start, children: [ Icon( Icons.photo_library, ), Container( margin: EdgeInsets.only(left: 5.0), child: Text( S.of(context).gallery, ), ), ], ), onPressed: () { getPictureFromGallery2(context, imageId, onGotFile); }, ), ), Container( child: TextButton( child: Row( mainAxisAlignment: MainAxisAlignment.start, children: [ Icon( Icons.photo_camera, ), Container( margin: EdgeInsets.only(left: 5.0), child: Text( S.of(context).camera, ), ), ], ), onPressed: () { getPictureFromCamera2(context, imageId, onGotFile); }, ), ) ], ), ), ); } void getPictureFromGallery2(BuildContext context, int imageId, OnGotFile onGotFile) async { ImagePicker picker = ImagePicker(); var image = await picker.pickImage(source: ImageSource.gallery); Navigator.of(context).pop(); onGotFile(imageId, image!.path); } void getPictureFromCamera2(BuildContext context, int imageId, OnGotFile onGotFile) async { ImagePicker picker = ImagePicker(); var image = await picker.pickImage(source: ImageSource.camera); Navigator.of(context).pop(); onGotFile(imageId, image!.path); } Future createTicket(BuildContext context, String msg, List> images, OnSuccess onSuccess, OnError onError, {int? id}) async { var formData = FormData(); formData.fields.add(MapEntry("msg", msg)); formData.fields.add(MapEntry('id', id == null ? '0' : id.toString())); for (int i = 0; i < images.length; i++) { String imagePath = images[i]['path']; if (imagePath.isNotEmpty) { String imageName = basename(imagePath); formData.files.add( MapEntry( "files", MultipartFile.fromFileSync(imagePath, filename: imageName) ) ); } } HttpUtil.httpPost('create-new-ticket-app/', (response) { if (onSuccess != null) { onSuccess(response); } }, isFormData: true, // body: null, formData: formData, sendProgress: (int sent, int total) { if (sent == total) { eventBus.fire(OnSubmitProgressEvent(false, 100.0)); } else { eventBus.fire(OnSubmitProgressEvent(true, sent / total * 100.0)); } }, businessId: Constants.BUSINESS_ID, ).catchError((error) { if (onError != null) { onError(error); } }); } Widget getNativePay(BuildContext context, Order order, PaymentPlatform paymentPlatform) { return GestureDetector( child: Container( padding: EdgeInsets.only(top: 16.0, left: 16.0, right: 16.0, bottom: 16.0), child: Center( child: Row( mainAxisAlignment: MainAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ Container( margin: EdgeInsets.only(right: 6.0), child: Text( Platform.isAndroid ? S.of(context).pay_with : S.of(context).pay_with, style: TextStyle( fontSize: 18.0, fontWeight: FontWeight.bold, ), ), ), Container( padding: EdgeInsets.only(right: 10.0), child: Platform.isAndroid ? Image.asset( 'assets/images/google_pay.png', height: 22.0, fit: BoxFit.fitHeight, ) : Image.asset( 'assets/images/apple_pay.png', height: 22.0, fit: BoxFit.fitHeight, ), ), ], ), ), decoration: BoxDecoration( border: Border( bottom: BorderSide( width: 10, color: Colors.black26, ), ), ), ), onTap: () { print(paymentPlatform); PaymentPlatform newPaymentPlatform = PaymentPlatform.fromJson(json.decode(json.encode(paymentPlatform))); newPaymentPlatform.code = Constants.PAYMENT_METHOD_NATIVE_PAY; goPayment(context, order, newPaymentPlatform); }, ); } static void goPayment(BuildContext context, Order order, PaymentPlatform paymentPlatform, { bool googlePay=false, bool applePay=false, StripePaymentMethod? stripePaymentMethod, }) { switch(paymentPlatform.code) { case Constants.PAYMENT_METHOD_CODE_SQUARE: break; case Constants.PAYMENT_METHOD_CODE_CHASE: break; case Constants.PAYMENT_METHOD_CODE_PAYPAL: break; case Constants.PAYMENT_METHOD_CODE_OTT_ALIPAY: break; case Constants.PAYMENT_METHOD_CODE_OTT_WECHATPAY: break; case Constants.PAYMENT_METHOD_CODE_POD: break; case Constants.PAYMENT_METHOD_NATIVE_PAY: StripePayment.setOptions( StripeOptions(publishableKey: paymentPlatform.publishableKey, merchantId: paymentPlatform.merchantId, androidPayMode: paymentPlatform.publishableKey!.contains('_test_') ? 'test' : 'production' ) ); StripePayment.paymentRequestWithNativePay( androidPayOptions: AndroidPayPaymentRequest( totalPrice: order.totalPrice.toStringAsFixed(2), currencyCode: order.businessInfo!.currency, ), applePayOptions: ApplePayPaymentOptions( currencyCode: order.businessInfo!.currency, countryCode: order.businessInfo!.address.country, items: [ ApplePayItem( label: order.businessInfo!.name, amount: order.totalPrice.toStringAsFixed(2), ), ], ), ).then((token) { print('return native token: ${token.tokenId}'); processNativePay(context, token, order); }).catchError((error) { print('native pay error: $error'); }); break; case Constants.PAYMENT_METHOD_CODE_STRIPE: Navigator.pushReplacement(context, MaterialPageRoute( builder: (BuildContext context) { return StripePay(order, paymentPlatform, stripePaymentMethod: stripePaymentMethod,); } )); break; } } static void processNativePay(BuildContext context, Token token, Order order) async { PaymentMethod paymentMethod = await StripePayment.createPaymentMethod( PaymentMethodRequest( card: CreditCard( token: token.tokenId, ), ), ); if (paymentMethod != null) { Utils.stripePaymentIntent(order, null, paymentMethod.id!, paymentMethod.type!, (response) { if (response.data['status'] == Constants.STRIPE_STATUS_REQUIRES_CONFIRMATION) { StripePayment.confirmPaymentIntent( PaymentIntent( clientSecret: response.data[Constants.STRIPE_CLIENT_SECRET], paymentMethodId: response.data['payment_method'], ), ).then((paymentIntentResult) { if (paymentIntentResult.status == Constants.STRIPE_STATUS_SUCCEDED) { Utils.stripeChargedSuccess(order, paymentMethod.id!, paymentIntentResult.paymentIntentId!, (response) { StripePayment.completeNativePayRequest().then((_) { eventBus.fire(OnOrderUpdated()); Routes.router.navigateTo(context, '/orderdetail/${order.id}', replace: true); }).catchError((error) { Utils.showMessageDialog(context, error, onOk: () { Navigator.of(context).pop(); }); }); }, (error) { Utils.showMessageDialog(context, error, onOk: () { Navigator.of(context).pop(); }); } ); } else { Utils.showMessageDialog(context, Exception('Unknown error')); } }).catchError((error) { Utils.showMessageDialog(context, error, onOk: () { Navigator.of(context).pop(); }); }); } }, (error) { Utils.showMessageDialog(context, error, onOk: () { Navigator.of(context).pop(); }); }); } else { } } static Future 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(); } }