import 'dart:async'; import 'dart:convert'; import 'package:dio/dio.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_spinkit/flutter_spinkit.dart'; import 'package:flutter_wisetronic/models/product.dart'; import 'package:fluttertoast/fluttertoast.dart'; import 'package:hive/hive.dart'; import 'package:intl/intl.dart'; import 'package:path/path.dart' as path; import 'package:universal_io/io.dart'; import 'package:url_launcher/url_launcher.dart'; import '../constants.dart'; import '../events/eventbus.dart'; import '../events/events.dart'; import '../generated/l10n.dart'; import '../models/business.dart'; import '../models/cart_info.dart'; import '../models/cart_line_item.dart'; import '../models/order.dart'; import '../models/user.dart'; import '../routes.dart'; import '../store/actions.dart'; import '../store/store.dart'; import 'http_util.dart'; import 'util_web.dart' if (dart.library.io) 'util_io.dart'; typedef void OnSuccess(Response response); typedef void OnError(dynamic error); typedef void OnComplete(dynamic data); typedef void OnOk(); typedef void OnLoadImageError(); class Utils { static StreamSubscription onProductWillAddToCartSubscription; static StreamSubscription onProductWillRemoveFromCartSubscription; init() async { onProductWillAddToCartSubscription = eventBus.on().listen((event) { CartInfo cartInfo = getCartInfoByBusiness(store.state.cartInfos, event.business); if (cartInfo == null || cartInfo.productList.length == 0) { cartInfo = CartInfo(); cartInfo.id = 0; cartInfo.amountPaid = 0.0; cartInfo.businessInfo = event.business; cartInfo.extraFeeList = []; cartInfo.discountList = []; CartLineItem lineItem = newCartLineItem( id: 0, price: event.price, product: event.product, name: event.product.name, description: event.description, quantity: 1.0); cartInfo.productList = [lineItem]; addSubproductToCard(cartInfo, lineItem); store.dispatch(new UpdateCartInfo(addCartInfoToCartInfoList( store.state.cartInfos, cartInfo))); eventBus.fire(new OnCartInfoUpdated()); print('#1'); } else { if (event.product.productAttributes.length > 0) { CartLineItem lineItem = newCartLineItem( id: 0, price: event.price, product: event.product, name: event.product.name, description: event.description, quantity: 1.0); cartInfo.productList.add(lineItem); addSubproductToCard(cartInfo, lineItem); store.dispatch(new UpdateCartInfo(addCartInfoToCartInfoList( store.state.cartInfos, cartInfo))); print('#2'); eventBus.fire(new OnCartInfoUpdated()); } else { int found = -1; for (var i = 0; i < cartInfo.productList.length; i++) { if (event.product.id == cartInfo.productList[i].product.id) { found = i; break; } } if (found == -1) { CartLineItem lineItem = newCartLineItem( id: 0, price: event.price, product: event.product, name: event.product.name, description: event.description, quantity: 1.0); cartInfo.productList.add(lineItem); addSubproductToCard(cartInfo, lineItem); store.dispatch(new UpdateCartInfo(addCartInfoToCartInfoList( store.state.cartInfos, cartInfo))); print('#3'); eventBus.fire(new OnCartInfoUpdated()); } else { if (cartInfo.productList[found].quantity + 1.0 > event.product.leftNum) { // Fluttertoast.showToast( // msg: S.of(context).product_insufficient, // toastLength: Toast.LENGTH_SHORT, // gravity: ToastGravity.CENTER, // backgroundColor: Colors.red, // textColor: Colors.white); } else { cartInfo.productList[found].quantity += 1; addSubproductQty(cartInfo, cartInfo.productList[found]); store.dispatch(new UpdateCartInfo( addCartInfoToCartInfoList( store.state.cartInfos, cartInfo))); print('#4'); eventBus.fire(new OnCartInfoUpdated()); } } } } }); onProductWillRemoveFromCartSubscription = eventBus.on().listen((event) { CartInfo cartInfo = getCartInfoByBusiness(store.state.cartInfos, event.business); if (cartInfo != null) { if (cartInfo.productList.length > 0) { if (event.productListIndex != -1) { if (cartInfo.productList[event.productListIndex].quantity <= 1) { String uuid = cartInfo.productList[event.productListIndex].uuid; cartInfo.productList.removeAt(event.productListIndex); removeSubproduct(cartInfo, uuid); } else { cartInfo.productList[event.productListIndex].quantity -= 1; addSubproductQty(cartInfo, cartInfo.productList[event.productListIndex], remove: true); } } else { int productListIndex = -1; for (var i = 0; i < cartInfo.productList.length; i++) { if (cartInfo.productList[i].product.id == event.product.id) { productListIndex = i; break; } } if (productListIndex != -1) { if (cartInfo.productList[productListIndex].quantity <= 1) { String uuid = cartInfo.productList[productListIndex].uuid; cartInfo.productList.removeAt(productListIndex); removeSubproduct(cartInfo, uuid); } else { cartInfo.productList[productListIndex].quantity -= 1; addSubproductQty(cartInfo, cartInfo.productList[productListIndex], remove: true); } } } } if (cartInfo.productList.length <= 0) { store.dispatch(UpdateCartInfo(removeCartInfoFromCartInfoList( store.state.cartInfos, cartInfo))); } else { store.dispatch(UpdateCartInfo(addCartInfoToCartInfoList( store.state.cartInfos, cartInfo))); } eventBus.fire(new OnCartInfoUpdated()); } }); } static bool equalsIgnoreCase(String a, String b) => (a == null && b == null) || (a != null && b != null && a.toLowerCase() == b.toLowerCase()); static int selectionsContains( Map selections, String key, String name) { if (selections.containsKey(key.toUpperCase())) { for (var i = 0; i < (selections[key.toUpperCase()] as List).length; i++) { Map item = (selections[key.toUpperCase()] as List)[i]; if (Utils.equalsIgnoreCase(item['name'], name)) { return i; } } } return -1; } static List getSelectedAttributeValue( Map selections, String key) { List valueArr = []; if (selections.containsKey(key.toUpperCase())) { for (var i = 0; i < (selections[key.toUpperCase()] as List).length; i++) { valueArr.add( (selections[key.toUpperCase()][i]['name'] as String).toLowerCase()); } } return valueArr; } static bool selectionsNotEmptyAt( Map selections, String key) { if (selections.containsKey(key.toUpperCase()) && (selections[key.toUpperCase()] as List).length > 0) { return true; } return false; } static Map stringToJson(String string) { if (string == null || string.isEmpty) { return null; } return json.decode(string); } static void jsonPrettyPrint(Map map) { JsonEncoder encoder = new JsonEncoder.withIndent(' '); String prettyPrint = encoder.convert(map); debugPrint(prettyPrint); } static launchURL(String url) async { if (await canLaunch(url)) { await launch(url); } else { throw 'Could not launch $url'; } } static String getPlatformName() { String platformName = ''; if (kIsWeb) { platformName = 'web'; } else if (Platform.isAndroid) { platformName = 'android'; } else if (Platform.isIOS) { platformName = 'ios'; } return platformName; } static Future getBox() async { return Util.getBox(); } static Widget imageLoadingIndicator( {double width = 30.0, double height = 30.0}) { return SizedBox( width: width, height: height, child: Center( child: Container( width: 30.0, height: 30.0, child: CupertinoActivityIndicator(), color: Colors.transparent, ), ), ); } static String safePhoneNumber(String phone) { if (phone.length < 8) { return phone; } String start = phone.substring(0, 3); String end = phone.substring(phone.length - 3); return '${start}****${end}'; } static String safeString(String string) { if (string == null || string.length == 0) { return ''; } String start = string.substring(0, 1); String end = string.substring(string.length - 1); return '${start}****${end}'; } static String smartRound(double amount, int decimalPlace) { double a = double.parse(amount.toStringAsFixed(decimalPlace)); double b = double.parse(amount.toStringAsFixed(0)); if (a - b == 0) { return amount.toStringAsFixed(0); } return amount.toStringAsFixed(decimalPlace); } static void createOrUpdateStripePaymentMethod( String paymentMethodId, String cardBrand, String paymentMethodType, String cardCountry, int cardExpMonth, int cardExpYear, String cardFunding, String cardLast4, ) { HttpUtil.httpPost( 'v1/create-update-stripe-payment-method', (response) { if (response.statusCode == 200) { print( 'create or update customer stripe payment method success. ${response.data}'); } }, body: { 'payment_method_id': paymentMethodId, 'card_brand': cardBrand, 'payment_method_type': paymentMethodType, 'card_country': cardCountry, 'card_exp_month': cardExpMonth, 'card_exp_year': cardExpYear, 'card_funding': cardFunding, 'card_last4': cardLast4, }, isFormData: true, ).catchError((error) { print('Error: ${error}'); }); } static showSubmitDialog(BuildContext context) { showDialog( context: context, barrierDismissible: false, builder: (BuildContext context) { return AlertDialog( content: WillPopScope( child: Container( width: 300.0, height: 150.0, child: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ SpinKitThreeBounce( color: Colors.lightBlueAccent, size: 30.0, ), Text( S.of(context).submitting, ), ], ), ), ), onWillPop: () async { Fluttertoast.showToast( msg: S.of(context).submitting_please_wait, toastLength: Toast.LENGTH_SHORT, gravity: ToastGravity.CENTER, backgroundColor: Colors.red, textColor: Colors.white); return false; }), ); }, ); } static showLoadingDialog(BuildContext context, {String message}) { showDialog( context: context, barrierDismissible: false, builder: (BuildContext context) { return AlertDialog( content: WillPopScope( child: Container( width: 80.0, height: 80.0, child: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ SpinKitThreeBounce( color: Colors.lightBlueAccent, size: 30.0, ), Text( message != null ? message : S.of(context).recalculating, ), ], ), ), ), onWillPop: () async { Fluttertoast.showToast( msg: S.of(context).loading_please_wait, toastLength: Toast.LENGTH_SHORT, gravity: ToastGravity.CENTER, backgroundColor: Colors.red, textColor: Colors.white); return false; }), ); }, ); } static getTitleFromBody(String body) { if (body.length <= 30) { return body; } return body.substring(0, 29); } static void getMiniLink( String realLink, OnSuccess onSuccess, OnError onError) { HttpUtil.httpPost( 'get-minilink/', (response) { onSuccess(response); }, body: {'link': realLink}, isFormData: true, ).catchError((error) { onError(error); }); } static String getOs({bool checkWeb = false}) { if (checkWeb && kIsWeb) { return 'web'; } if (Platform.isAndroid) { return 'android'; } if (Platform.isIOS) { return 'ios'; } if (Platform.isLinux) { return 'ubuntu'; } if (Platform.isMacOS) { return 'mac'; } if (Platform.isWindows) { return 'windows'; } return 'web'; } static int getOsFontHex(String os) { if (os == 'mac') { return Constants.FONT_APPLE; } if (os == 'windows') { return Constants.FONT_WINDOWS; } if (os == 'ubuntu') { return Constants.FONT_UBUNTU; } if (os == 'android') { return Constants.FONT_ANDROID; } if (os == 'ios') { return Constants.FONT_IOS; } return Constants.FONT_TOPTONS; } static void getCurrentUser() { getBox().then((box) { int userId = box.get(Constants.KEY_USER_ID, defaultValue: 0); String accessToken = box.get(Constants.KEY_ACCESS_TOKEN, defaultValue: ''); if (userId == 0 || accessToken.isEmpty) { eventBus.fire(new OnGetCurrentUserFailed(Error())); } else { HttpUtil.httpGet( 'v1/users/$userId', queryParameters: {}, businessId: Constants.BUSINESS_ID, ).then((value) { User user = User.fromJson(value); store.dispatch(UpdateCurrentUser(user)); eventBus.fire(OnCurrentUserUpdated()); }).catchError((err) { eventBus.fire(new OnGetCurrentUserFailed(err)); }); } }).catchError((err) { eventBus.fire(new OnGetCurrentUserFailed(err)); }); } static void showMessageDialog(BuildContext context, dynamic error, {String title, List actions, OnOk onOk}) { String message = ''; print('error $error'); if (error is DioError && error.response != null) { if (error.response.data['message'] != null) { message = error.response.data['message']; } else if (error.response.data['error'] != null) { if (error.response.data['error'] is String) { message = error.response.data['error']; } else if (error.response.data['error']['errmsg'] != null) { message = error.response.data['error']['errmsg']; } else if (error.response.data['error']['message'] != null) { message = error.response.data['error']['message']; } else { message = error.response.data.toString(); } } } else if (error is String) { message = '$error'; } else if (error.message != null) { message = error.message; } else { message = '$error'; } showDialog( context: context, builder: (BuildContext context) { return AlertDialog( title: Text(title != null ? title : S.of(context).error), content: Text(message), actions: actions == null ? [ TextButton( child: Text(S.of(context).ok), onPressed: onOk == null ? () { Navigator.of(context).pop(); } : onOk, ), ] : actions, ); }); } static Future> getLastVisit() async { Box box = await getBox(); List lastVisit = json.decode(box.get(Constants.KEY_LAST_VISIT, defaultValue: '[]')); List lv = lastVisit.map((e) => e as int).toList(); store.dispatch(UpdateLastVisit(lv)); return lv; } static void saveLastVisit(List lastVisit) { getBox().then((box) { box.put(Constants.KEY_LAST_VISIT, lastVisit.toString()); }).catchError((error) { print('Error occurred: $error'); }); } static String utcDatetimeStringToLocalDatetimeString( BuildContext context, String utcDatetimeString, {bool withTime = false}) { var utcDatetime; try { utcDatetime = DateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'") .parse(utcDatetimeString, true); } catch (error) { utcDatetime = DateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'") .parse(utcDatetimeString, true); } var localDatetime = utcDatetime.toLocal(); return datetimeFormat(context, localDatetime, withTime: withTime); } static String datetimeFormat(BuildContext context, DateTime date, {bool withTime = false}) { final theDate = DateTime(date.year, date.month, date.day); final now = DateTime.now(); final today = DateTime(now.year, now.month, now.day); final yesterday = DateTime(now.year, now.month, now.day - 1); final tomorrow = DateTime(now.year, now.month, now.day + 1); DateFormat formatter = DateFormat('yyyy/MM/dd'); DateFormat timeFormatter = DateFormat('HH:mm'); if (withTime != null && withTime) { formatter = DateFormat('yyyy/MM/dd HH:mm:ss'); } if (theDate == today) { return S.of(context).today_with_time(timeFormatter.format(date)); } if (theDate == tomorrow) { return S.of(context).tomorrow_with_time(timeFormatter.format(date)); } return formatter.format(date); } void uploadPicture( BuildContext context, File image, Map product, int imageId, String currentImageUrl, OnSuccess onSuccess, {int originalImageId = 0}) async { if (image != null && await image.exists()) { print('image: ${image.path}'); String imageName = path.basename(image.path); MultipartFile imageFile = await MultipartFile.fromFile(image.path, filename: imageName); Map formMap = { 'id': product['id'], 'image': imageFile, 'original_image_id': originalImageId }; if (imageId == -1) { HttpUtil.httpPost( 'upload-product-image/', (response) { if (onSuccess != null) { onSuccess(response); } }, isFormData: true, body: formMap, sendProgress: (int sent, int total) { if (sent == total) { eventBus.fire(OnUploadImageProgressEvent(imageId, false, 1.0)); } else { eventBus.fire( OnUploadImageProgressEvent(imageId, true, sent / total)); } }, ).catchError((error) { Utils.showMessageDialog(context, error); }); } else { HttpUtil.httpPost( 'upload-product-gallery-image/', (response) { if (onSuccess != null) { onSuccess(response); } }, isFormData: true, body: formMap, sendProgress: (int sent, int total) { if (sent == total) { eventBus.fire(OnUploadImageProgressEvent(imageId, false, 0.0)); } else { eventBus.fire( OnUploadImageProgressEvent(imageId, true, sent / total)); } }, ).catchError((error) { Utils.showMessageDialog(context, error); }); } } } static CartInfo getCartInfoByBusiness( List cartInfos, Business business) { if (cartInfos == null || cartInfos.length <= 0) { return null; } for (var i = 0; i < cartInfos.length; i++) { if (cartInfos[i].businessInfo.id == business.id) { return cartInfos[i]; } } return null; } static CartInfo getCartInfoByBusinessId( List cartInfos, int businessId) { if (cartInfos == null || cartInfos.length <= 0) { return null; } for (var i = 0; i < cartInfos.length; i++) { if (cartInfos[i].businessInfo.id == businessId) { return cartInfos[i]; } } return null; } static List addCartInfoToCartInfoList( List cartInfos, CartInfo cartInfo) { if (cartInfos == null || cartInfos.length <= 0) { cartInfos = [cartInfo]; } else { bool found = false; for (var i = 0; i < cartInfos.length; i++) { if (cartInfos[i].businessInfo.id == cartInfo.businessInfo.id) { cartInfos[i] = cartInfo; found = true; break; } } if (!found) { cartInfos.add(cartInfo); } } Utils.getBox().then((box) { box.put(Constants.KEY_CARTINFOS, cartInfos.toString()); }).catchError((error) { print('Error occurred: $error'); }); return cartInfos; } static List removeCartInfoFromCartInfoList( List cartInfos, CartInfo cartInfo) { if (cartInfos == null || cartInfos.length <= 0 || cartInfo == null) { return []; } int removeIndex = -1; for (var i = 0; i < cartInfos.length; i++) { if (cartInfo.businessInfo.id == cartInfos[i].businessInfo.id) { removeIndex = i; break; } } if (removeIndex != -1) { cartInfos.removeAt(removeIndex); } getBox().then((box) { box.put(Constants.KEY_CARTINFOS, cartInfos.toString()); }).catchError((error) { print('Error occurred: $error'); }); return cartInfos; } static void clearCart(BuildContext context, CartInfo cartInfo, {OnComplete onComplete}) { if (cartInfo != null && cartInfo.productList.length > 0) { showDialog( context: context, builder: (BuildContext context) { return AlertDialog( title: Text(S.of(context).warning), content: Text(S.of(context).are_you_sure_to_empty_basket), actions: [ TextButton( child: Text(S.of(context).cancel), onPressed: () { Navigator.of(context).pop(); }, ), TextButton( child: Text(S.of(context).yes_i_am_sure), onPressed: () { store.dispatch(new UpdateCartInfo( Utils.removeCartInfoFromCartInfoList( store.state.cartInfos, cartInfo))); eventBus.fire(new OnCartInfoUpdated()); Navigator.of(context).pop(); if (onComplete != null) { onComplete(null); } }, ) ], ); }); } } static void requireLogin(BuildContext context, {String returnUrl, bool replace = true, int delayMilliseconds = 400}) { Future.delayed(Duration(milliseconds: delayMilliseconds), () { if (returnUrl != null) { store.dispatch(UpdateRedirectRoute(returnUrl)); } Routes.router.navigateTo(context, '/login', replace: replace); }); } static String getFirstNumberFromString(String numString) { final intInStr = RegExp(r'\d+'); return intInStr.firstMatch(numString).group(0); } static void loadProducts(int businessId, int categoryId, int page, bool more, OnComplete onComplete, OnError onError, {int featuredCount=4, int hotSaleCount=4, String keyword=''}) { print('feature $featuredCount'); HttpUtil.httpGet( more ? 'v1/service-category-products-more' : 'v1/service-category-products', businessId: businessId, queryParameters: { // 'lat': store.state.position.latitude.toString(), // 'lng': store.state.position.longitude.toString(), 'category_id': categoryId, 'preview': Constants.isPreview, 'page': page, 'num_per_page': Constants.ORDERS_PER_PAGE, 'featured_count': featuredCount, 'hot_sale_count': hotSaleCount, 'keyword': Uri.encodeComponent(keyword), }, ).then((value) { if (onComplete != null) { onComplete(value); } }).catchError((error) { if (onError != null) { onError(error); } }); } static String timestampToString(BuildContext context, int timestamp, {bool withTime=false}) { DateTime date = DateTime.fromMillisecondsSinceEpoch(timestamp * 1000); if (timestamp == 0) { return ''; } return datetimeFormat(context, date, withTime: withTime); } static String errorMsg2(BuildContext context, String code, String defaultString) { switch (code) { case 'no_delivery': return S.of(context).checkout_no_deliver; case 'no_shipping_address': return S.of(context).please_provide_shipping_address; case 'over_delivery_distance': return S.of(context).over_delivery_distance; case 'amount_not_meet': return S.of(context).amount_not_meet; case 'select_canada_post_shipping_rate': return S.of(context).select_canada_post_shipping_rate; case 'store_close': return S.of(context).store_closed; } return defaultString; } static String knownName(BuildContext context, String name) { switch (name) { case 'pickup-discount': return S.of(context).pickup_discount; case 'collect-tips': return S.of(context).service_fee; default: return name; } } static String getWeekDayName(BuildContext context, int timestamp, bool shortName) { DateTime date = DateTime.fromMillisecondsSinceEpoch(timestamp * 1000); switch(date.weekday) { case 1: if (shortName) { return S.of(context).mon; } else { return S.of(context).monday; } break; case 2: if (shortName) { return S.of(context).tue; } else { return S.of(context).tuesday; } break; case 3: if (shortName) { return S.of(context).wed; } else { return S.of(context).wednesday; } break; case 4: if (shortName) { return S.of(context).thu; } else { return S.of(context).thursday; } break; case 5: if (shortName) { return S.of(context).fri; } else { return S.of(context).friday; } break; case 6: if (shortName) { return S.of(context).sat; } else { return S.of(context).saturday; } break; case 7: if (shortName) { return S.of(context).sun; } else { return S.of(context).sunday; } break; } } static void stripePaymentIntent(Order order, String customerId, String paymentMethodId, String cardType, OnSuccess onSuccess, OnError onError, { String cardBrand, String cardCountry, int cardExpMonth, int cardExpYear, String cardFunding, String cardLast4, }) { HttpUtil.httpPost('v1/stripe-payment-intent', onSuccess, body: { 'order_id': order.id, 'pay_amount': order.totalPrice, 'customer_id': customerId == null ? '' : customerId, 'payment_method_id': paymentMethodId, 'payment_method_type': cardType, 'card_brand': cardBrand, 'card_country': cardCountry, 'card_exp_month': cardExpMonth, 'card_exp_year': cardExpYear, 'card_funding': cardFunding, 'card_last4': cardLast4, }, isFormData: true, ).catchError(onError); } static void stripeChargedSuccess(Order order, String paymentMethodId, String paymentIntentId, OnSuccess onSuccess, OnError onError) { HttpUtil.httpPost('v1/stripe-charged-success', onSuccess, isFormData: true, body: { 'order_id': order.id, 'payment_method_id': paymentMethodId, 'payment_intent_id': paymentIntentId, } ).catchError(onError); } static int getProductLineInOrder(CartInfo cartInfo) { int qty = 0; for (CartLineItem lineItem in cartInfo.productList) { if (lineItem.product.id != null) { qty += 1; } } return qty; } static void orderAgain(BuildContext context, CartInfo cartInfo) { cartInfo.productList.removeWhere((element) => element.product == null || element.product.id == null); store.dispatch(UpdateCartInfo(Utils.addCartInfoToCartInfoList(store.state.cartInfos, cartInfo))); eventBus.fire(new OnCartInfoUpdated()); Routes.router.navigateTo(context, '/checkout/${cartInfo.businessInfo.id}'); } static String getOrderStatus(BuildContext context, int statusCode) { switch(statusCode) { case Constants.STATUS_PENDING: return S.of(context).pending; case Constants.STATUS_ACCEPT: return S.of(context).order_acceipt; case Constants.STATUS_PROCESSING: return S.of(context).order_processing; case Constants.STATUS_COMPLETE: return S.of(context).order_complete; case Constants.STATUS_CANCELLED: return S.of(context).order_cancelled; default: return S.of(context).pending; } } static CartLineItem newCartLineItem( {int id, double price, Product product, String name, String description, double quantity, String parentUuid, }) { CartLineItem lineItem = CartLineItem(); lineItem.unitPrice = price; lineItem.product = product; lineItem.name = product.name; lineItem.description = description; lineItem.quantity = quantity; lineItem.parentUuid = parentUuid; return lineItem; } static void addSubproductToCard(CartInfo cartInfo, CartLineItem lineItem) { if (lineItem.product.subproducts.length > 0) { for (int j = 0; j < lineItem.product.subproducts.length; j++) { cartInfo.productList.add( newCartLineItem( id: 0, price: 0.0, product: Product( lineItem.product.subproducts[j].product.id, lineItem.product.businessId, lineItem.product.subproducts[j].product.name, lineItem.product.subproducts[j].product.price, lineItem.product.subproducts[j].product.description, lineItem.product.subproducts[j].product.image, lineItem.product.subproducts[j].product.productAttributes ), name: lineItem.product.subproducts[j].product.name, description: lineItem.product.subproducts[j].product.description, quantity: 1.0, parentUuid: lineItem.uuid ) ); } } } static void addSubproductQty(CartInfo cartInfo, CartLineItem lineItem, {bool remove = false}) { if (lineItem.product.subproducts.length > 0) { for (var i = 0; i < cartInfo.productList.length; i++) { CartLineItem lineItem1 = cartInfo.productList[i]; if (lineItem1.parentUuid == lineItem.uuid) { for (var j = 0; j < lineItem.product.subproducts.length; j++) { if (lineItem.product.subproducts[j].product.id == lineItem1.product.id) { if (remove) { lineItem1.quantity -= lineItem.product.subproducts[j].quantity; } else { lineItem1.quantity += lineItem.product.subproducts[j].quantity; } break; } } } } } } static void removeSubproduct(CartInfo cartInfo, String uuid) { cartInfo.productList.removeWhere((element) => element.parentUuid == uuid); } static void openEmail(String email) async { final Uri params = Uri( scheme: 'mailto', path: '$email', ); String url = params.toString(); if (await canLaunch(url)) { await launchURL(url); } else { print('Could not launch $url'); } } static void callPhone(String phone) async { String callNo = 'tel://$phone'; if (await canLaunch(callNo)) { await launchURL(callNo); } else { print('Could not call $phone'); } } static Widget buildLine(String name, dynamic value, {double nameSize, double valueSize}) { Row row = Row( mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [], ); row.children.add( Container( width: 150, padding: EdgeInsets.only(), child: Text( name, style: TextStyle( color: Colors.black38, fontSize: nameSize, ), ), ), ); if (value is String) { row.children.add( Expanded( child: Align( alignment: Alignment.centerRight, child: Text( value, style: TextStyle( fontSize: valueSize, ), ), ), ), ); } else { row.children.add( Expanded(child: value), ); } return Container( padding: EdgeInsets.only(top: 8, bottom: 8), child: row, ); } static int getBusinessId() { return Constants.BUSINESS_ID; } } class RuntimeError extends Error { final int code; final String message; RuntimeError(this.message, {this.code}); String toString() => "Runtime Error: $message"; }