Files
flutter_wisetronic/lib/utils/util_io.dart
2026-07-12 04:33:48 +08:00

528 lines
17 KiB
Dart

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: <Widget>[
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: <Widget>[
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: <Widget>[
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<String, dynamic> 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: <Widget>[
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: <Widget>[
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: <Widget>[
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<void> createTicket(BuildContext context, String msg, List<Map<String, dynamic>> 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: <Widget>[
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<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();
}
}