From 7e8182259f9d778bfa9294239578a4ced883d6ab Mon Sep 17 00:00:00 2001 From: peima Date: Fri, 24 Jul 2026 20:13:07 +0800 Subject: [PATCH] phase3: utils.dart null-safe (cart logic + dio5 error handling) - cart event handlers rewritten with local list + nullable model fields - getCartInfo*/addCart/removeCart helpers: nullable List? params - addSubproductToCard/addSubproductQty/removeSubproduct: null-safe nested access - showMessageDialog: DioException + promoted response local - newCartLineItem params nullable; getWeekDayName default return - launchURL->launch; RuntimeError code nullable --- lib/utils/utils.dart | 319 ++++++++++++++++++++----------------------- 1 file changed, 145 insertions(+), 174 deletions(-) diff --git a/lib/utils/utils.dart b/lib/utils/utils.dart index 32941ba..f9b83d0 100644 --- a/lib/utils/utils.dart +++ b/lib/utils/utils.dart @@ -37,89 +37,60 @@ typedef void OnOk(); typedef void OnLoadImageError(); class Utils { - static StreamSubscription onProductWillAddToCartSubscription; - static StreamSubscription onProductWillRemoveFromCartSubscription; + 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? found = getCartInfoByBusiness(store.state.cartInfos, event.business); + final List existing = + (found != null && found.productList != null && found.productList!.isNotEmpty) + ? found.productList! : []; + CartInfo cartInfo = found ?? CartInfo(); + if (existing.isEmpty) { 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); + 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'); + store.dispatch(UpdateCartInfo(addCartInfoToCartInfoList(store.state.cartInfos, cartInfo))); + eventBus.fire(OnCartInfoUpdated()); } else { - if (event.product.productAttributes.length > 0) { + if ((event.product.productAttributes?.length ?? 0) > 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); + id: 0, price: event.price, product: event.product, + name: event.product.name, description: event.description, quantity: 1.0); + existing.add(lineItem); addSubproductToCard(cartInfo, lineItem); - store.dispatch(new UpdateCartInfo(addCartInfoToCartInfoList( - store.state.cartInfos, cartInfo))); - print('#2'); - eventBus.fire(new OnCartInfoUpdated()); + store.dispatch(UpdateCartInfo(addCartInfoToCartInfoList(store.state.cartInfos, cartInfo))); + eventBus.fire(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; - } + int foundIndex = -1; + for (var i = 0; i < existing.length; i++) { + if (existing[i].product?.id == event.product.id) { foundIndex = i; break; } } - if (found == -1) { + if (foundIndex == -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); + id: 0, price: event.price, product: event.product, + name: event.product.name, description: event.description, quantity: 1.0); + existing.add(lineItem); addSubproductToCard(cartInfo, lineItem); - store.dispatch(new UpdateCartInfo(addCartInfoToCartInfoList( - store.state.cartInfos, cartInfo))); - print('#3'); - eventBus.fire(new OnCartInfoUpdated()); + store.dispatch(UpdateCartInfo(addCartInfoToCartInfoList(store.state.cartInfos, cartInfo))); + eventBus.fire(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); + if ((existing[foundIndex].quantity ?? 0) + 1.0 > (event.product.leftNum ?? double.infinity)) { + // insufficient stock } 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()); + existing[foundIndex].quantity = (existing[foundIndex].quantity ?? 0) + 1; + addSubproductQty(cartInfo, existing[foundIndex]); + store.dispatch(UpdateCartInfo(addCartInfoToCartInfoList(store.state.cartInfos, cartInfo))); + eventBus.fire(OnCartInfoUpdated()); } } } @@ -127,52 +98,45 @@ class Utils { }); onProductWillRemoveFromCartSubscription = eventBus.on().listen((event) { - CartInfo cartInfo = - getCartInfoByBusiness(store.state.cartInfos, event.business); - if (cartInfo != null) { - if (cartInfo.productList.length > 0) { + CartInfo? found = getCartInfoByBusiness(store.state.cartInfos, event.business); + if (found != null && found.productList != null) { + final existing = found.productList!; + if (existing.isNotEmpty) { 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); + if ((existing[event.productListIndex].quantity ?? 0) <= 1) { + String uuid = existing[event.productListIndex].uuid ?? ''; + existing.removeAt(event.productListIndex); + removeSubproduct(found, uuid); } else { - cartInfo.productList[event.productListIndex].quantity -= 1; - addSubproductQty(cartInfo, cartInfo.productList[event.productListIndex], remove: true); + existing[event.productListIndex].quantity = (existing[event.productListIndex].quantity ?? 0) - 1; + addSubproductQty(found, existing[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; - } + int idx = -1; + for (var i = 0; i < existing.length; i++) { + if (existing[i].product?.id == event.product.id) { idx = i; break; } } - if (productListIndex != -1) { - if (cartInfo.productList[productListIndex].quantity <= 1) { - String uuid = cartInfo.productList[productListIndex].uuid; - cartInfo.productList.removeAt(productListIndex); - removeSubproduct(cartInfo, uuid); + if (idx != -1) { + if ((existing[idx].quantity ?? 0) <= 1) { + String uuid = existing[idx].uuid ?? ''; + existing.removeAt(idx); + removeSubproduct(found, uuid); } else { - cartInfo.productList[productListIndex].quantity -= 1; - addSubproductQty(cartInfo, cartInfo.productList[productListIndex], remove: true); + existing[idx].quantity = (existing[idx].quantity ?? 0) - 1; + addSubproductQty(found, existing[idx], remove: true); } } } } - - if (cartInfo.productList.length <= 0) { - store.dispatch(UpdateCartInfo(removeCartInfoFromCartInfoList( - store.state.cartInfos, cartInfo))); + if (existing.isEmpty) { + store.dispatch(UpdateCartInfo(removeCartInfoFromCartInfoList(store.state.cartInfos, found))); } else { - store.dispatch(UpdateCartInfo(addCartInfoToCartInfoList( - store.state.cartInfos, cartInfo))); + store.dispatch(UpdateCartInfo(addCartInfoToCartInfoList(store.state.cartInfos, found))); } - eventBus.fire(new OnCartInfoUpdated()); + eventBus.fire(OnCartInfoUpdated()); } }); } - static bool equalsIgnoreCase(String a, String b) => (a == null && b == null) || (a != null && b != null && a.toLowerCase() == b.toLowerCase()); @@ -211,7 +175,7 @@ class Utils { return false; } - static Map stringToJson(String string) { + static Map? stringToJson(String string) { if (string == null || string.isEmpty) { return null; } @@ -332,7 +296,7 @@ class Utils { ); } - static showLoadingDialog(BuildContext context, {String message}) { + static showLoadingDialog(BuildContext context, {String? message}) { showDialog( context: context, barrierDismissible: false, @@ -460,21 +424,24 @@ class Utils { } static void showMessageDialog(BuildContext context, dynamic error, - {String title, List actions, OnOk onOk}) { + {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(); + if (error is DioException) { + final resp = error.response; + if (resp != null) { + if (resp.data['message'] != null) { + message = resp.data['message']; + } else if (resp.data['error'] != null) { + if (resp.data['error'] is String) { + message = resp.data['error']; + } else if (resp.data['error']['errmsg'] != null) { + message = resp.data['error']['errmsg']; + } else if (resp.data['error']['message'] != null) { + message = resp.data['error']['message']; + } else { + message = resp.data.toString(); + } } } } else if (error is String) { @@ -623,26 +590,26 @@ class Utils { } } - static CartInfo getCartInfoByBusiness( - List cartInfos, Business business) { + 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) { + if (cartInfos[i].businessInfo?.id == business.id) { return cartInfos[i]; } } return null; } - static CartInfo getCartInfoByBusinessId( - List cartInfos, int businessId) { + 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) { + if (cartInfos[i].businessInfo?.id == businessId) { return cartInfos[i]; } } @@ -650,13 +617,13 @@ class Utils { } static List addCartInfoToCartInfoList( - List cartInfos, CartInfo cartInfo) { + 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) { + if (cartInfos[i].businessInfo?.id == cartInfo.businessInfo?.id) { cartInfos[i] = cartInfo; found = true; break; @@ -675,13 +642,13 @@ class Utils { } static List removeCartInfoFromCartInfoList( - List cartInfos, CartInfo cartInfo) { + 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) { + if (cartInfo.businessInfo?.id == cartInfos[i].businessInfo?.id) { removeIndex = i; break; } @@ -697,9 +664,9 @@ class Utils { return cartInfos; } - static void clearCart(BuildContext context, CartInfo cartInfo, - {OnComplete onComplete}) { - if (cartInfo != null && cartInfo.productList.length > 0) { + static void clearCart(BuildContext context, CartInfo? cartInfo, + {OnComplete? onComplete}) { + if (cartInfo != null && cartInfo.productList != null && cartInfo.productList!.length > 0) { showDialog( context: context, builder: (BuildContext context) { @@ -733,7 +700,7 @@ class Utils { } static void requireLogin(BuildContext context, - {String returnUrl, bool replace = true, int delayMilliseconds = 400}) { + {String? returnUrl, bool replace = true, int delayMilliseconds = 400}) { Future.delayed(Duration(milliseconds: delayMilliseconds), () { if (returnUrl != null) { store.dispatch(UpdateRedirectRoute(returnUrl)); @@ -744,7 +711,7 @@ class Utils { static String getFirstNumberFromString(String numString) { final intInStr = RegExp(r'\d+'); - return intInStr.firstMatch(numString).group(0); + return intInStr.firstMatch(numString)?.group(0) ?? ''; } static void loadProducts(int businessId, int categoryId, int page, bool more, @@ -864,15 +831,18 @@ class Utils { return S.of(context).sunday; } break; + default: + return ''; } + return ''; } static int getProductLineInOrder(CartInfo cartInfo) { int qty = 0; - for (CartLineItem lineItem in cartInfo.productList) { - if (lineItem.product.id != null) { + for (CartLineItem lineItem in (cartInfo.productList ?? [])) { + if (lineItem.product?.id != null) { qty += 1; } } @@ -880,10 +850,10 @@ class Utils { } static void orderAgain(BuildContext context, CartInfo cartInfo) { - cartInfo.productList.removeWhere((element) => element.product == null || element.product.id == null); + 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}'); + Routes.router.navigateTo(context, '/checkout/${cartInfo.businessInfo?.id}'); } static String getOrderStatus(BuildContext context, int statusCode) { @@ -904,18 +874,18 @@ class Utils { } static CartLineItem newCartLineItem( - {int id, - double price, - Product product, - String name, - String description, - double quantity, - String parentUuid, + {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.name = product?.name; lineItem.description = description; lineItem.quantity = quantity; lineItem.parentUuid = parentUuid; @@ -923,46 +893,47 @@ class Utils { } 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 - ) - ); - } + final product = lineItem.product; + final subs = product?.subproducts; + if (subs == null || subs.isEmpty) return; + for (int j = 0; j < subs.length; j++) { + final sp = subs[j].product; + cartInfo.productList?.add(newCartLineItem( + id: 0, + price: 0.0, + product: Product( + sp?.id ?? 0, + product?.businessId ?? 0, + sp?.name ?? '', + sp?.price ?? 0.0, + sp?.description ?? '', + sp?.image ?? '', + sp?.productAttributes ?? [] + ), + name: sp?.name ?? '', + description: sp?.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; + final product = lineItem.product; + final subs = product?.subproducts; + if (subs == null || subs.isEmpty) return; + for (var i = 0; i < (cartInfo.productList?.length ?? 0); i++) { + CartLineItem lineItem1 = cartInfo.productList![i]; + if (lineItem1.parentUuid == lineItem.uuid) { + for (var j = 0; j < subs.length; j++) { + if (subs[j].product?.id == lineItem1.product?.id) { + if (remove) { + lineItem1.quantity = (lineItem1.quantity ?? 0) - (subs[j].quantity ?? 0); + } else { + lineItem1.quantity = (lineItem1.quantity ?? 0) + (subs[j].quantity ?? 0); } + break; } } } @@ -970,7 +941,7 @@ class Utils { } static void removeSubproduct(CartInfo cartInfo, String uuid) { - cartInfo.productList.removeWhere((element) => element.parentUuid == uuid); + cartInfo.productList?.removeWhere((element) => element.parentUuid == uuid); } static void openEmail(String email) async { @@ -980,7 +951,7 @@ class Utils { ); String url = params.toString(); if (await canLaunch(url)) { - await launchURL(url); + await launch(url); } else { print('Could not launch $url'); } @@ -989,13 +960,13 @@ class Utils { static void callPhone(String phone) async { String callNo = 'tel://$phone'; if (await canLaunch(callNo)) { - await launchURL(callNo); + await launch(callNo); } else { print('Could not call $phone'); } } - static Widget buildLine(String name, dynamic value, {double nameSize, double valueSize}) { + static Widget buildLine(String name, dynamic value, {double? nameSize, double? valueSize}) { Row row = Row( mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, @@ -1045,7 +1016,7 @@ class Utils { } class RuntimeError extends Error { - final int code; + final int? code; final String message; RuntimeError(this.message, {this.code}); String toString() => "Runtime Error: $message";