commit
b771c891fb
@ -1,3 +1,13 @@
|
|||||||
|
## [5.7.0] - 2022-01-29
|
||||||
|
|
||||||
|
* Refactor product detail page
|
||||||
|
* View HTML in description on the product detail page
|
||||||
|
* Allow upsell products to be viewed on the Product detail page (if enabled)
|
||||||
|
* Allow related products to be viewed on the Product detail page (if enabled)
|
||||||
|
* Allow product reviews to be view on the product page (if enabled)
|
||||||
|
* Flutter format in /resources
|
||||||
|
* Pubspec.yaml dependency updates
|
||||||
|
|
||||||
## [5.6.2] - 2022-01-07
|
## [5.6.2] - 2022-01-07
|
||||||
|
|
||||||
* Fix null return in CheckoutShippingTypeWidget
|
* Fix null return in CheckoutShippingTypeWidget
|
||||||
|
|||||||
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
# WooCommerce App: Label StoreMax
|
# WooCommerce App: Label StoreMax
|
||||||
|
|
||||||
### Label StoreMax - v5.6.2
|
### Label StoreMax - v5.7.0
|
||||||
|
|
||||||
|
|
||||||
[Official WooSignal WooCommerce App](https://woosignal.com)
|
[Official WooSignal WooCommerce App](https://woosignal.com)
|
||||||
|
|||||||
@ -210,5 +210,14 @@
|
|||||||
"This product has been removed from your wishlist": "Dieses Produkt wurde von Ihrer Wunschliste entfernt",
|
"This product has been removed from your wishlist": "Dieses Produkt wurde von Ihrer Wunschliste entfernt",
|
||||||
"This product has been added to your wishlist": "Dieses Produkt wurde Ihrer Wunschliste hinzugefügt",
|
"This product has been added to your wishlist": "Dieses Produkt wurde Ihrer Wunschliste hinzugefügt",
|
||||||
"Spend a minimum of minimumAmount to redeem": "Verbringen Sie mindestens {{minimumAmount}} einlösen",
|
"Spend a minimum of minimumAmount to redeem": "Verbringen Sie mindestens {{minimumAmount}} einlösen",
|
||||||
"Spend less than maximumAmount to redeem": "Geben Sie weniger aus als {{maximumAmount}} einlösen"
|
"Spend less than maximumAmount to redeem": "Geben Sie weniger aus als {{maximumAmount}} einlösen",
|
||||||
|
"Related products": "Verwandte Produkte",
|
||||||
|
"Reviews": "Bewertungen",
|
||||||
|
"There are no reviews yet.": "Es gibt noch keine Bewertungen.",
|
||||||
|
"More": "Mehr",
|
||||||
|
"You may also like": "Sie können auch mögen",
|
||||||
|
"Leave a review": "Hinterlassen Sie eine Bewertung",
|
||||||
|
"How would you rate": "Wie beurteilen Sie",
|
||||||
|
"Submit": "einreichen",
|
||||||
|
"Your review has been submitted": "Ihre Bewertung wurde übermittelt"
|
||||||
}
|
}
|
||||||
@ -210,5 +210,14 @@
|
|||||||
"Wishlist": "Wishlist",
|
"Wishlist": "Wishlist",
|
||||||
"No items found": "No items found",
|
"No items found": "No items found",
|
||||||
"This product has been removed from your wishlist": "This product has been removed from your wishlist",
|
"This product has been removed from your wishlist": "This product has been removed from your wishlist",
|
||||||
"This product has been added to your wishlist": "This product has been added to your wishlist"
|
"This product has been added to your wishlist": "This product has been added to your wishlist",
|
||||||
|
"Related products": "Related products",
|
||||||
|
"Reviews": "Reviews",
|
||||||
|
"There are no reviews yet.": "There are no reviews yet.",
|
||||||
|
"More": "More",
|
||||||
|
"You may also like": "You may also like",
|
||||||
|
"Leave a review": "Leave a review",
|
||||||
|
"How would you rate": "How would you rate",
|
||||||
|
"Submit": "Submit",
|
||||||
|
"Your review has been submitted": "Your review has been submitted"
|
||||||
}
|
}
|
||||||
@ -210,5 +210,14 @@
|
|||||||
"This product has been removed from your wishlist": "Este producto ha sido eliminado de tu lista de deseos.",
|
"This product has been removed from your wishlist": "Este producto ha sido eliminado de tu lista de deseos.",
|
||||||
"This product has been added to your wishlist": "Este producto ha sido añadido a su lista de deseos",
|
"This product has been added to your wishlist": "Este producto ha sido añadido a su lista de deseos",
|
||||||
"Spend a minimum of minimumAmount to redeem": "Gasta un mínimo de {{minimumAmount}} para redimir",
|
"Spend a minimum of minimumAmount to redeem": "Gasta un mínimo de {{minimumAmount}} para redimir",
|
||||||
"Spend less than maximumAmount to redeem": "Gasta menos de {{maximumAmount}} para redimir"
|
"Spend less than maximumAmount to redeem": "Gasta menos de {{maximumAmount}} para redimir",
|
||||||
|
"Related products": "Productos relacionados",
|
||||||
|
"Reviews": "Reseñas",
|
||||||
|
"There are no reviews yet.": "Aún no hay reseñas.",
|
||||||
|
"More": "Más",
|
||||||
|
"You may also like": "También te puede interesar",
|
||||||
|
"Leave a review": "Dejar un comentario",
|
||||||
|
"How would you rate": "Cómo calificarías",
|
||||||
|
"Submit": "Entregar",
|
||||||
|
"Your review has been submitted": "Tu reseña ha sido enviada"
|
||||||
}
|
}
|
||||||
@ -210,5 +210,14 @@
|
|||||||
"This product has been removed from your wishlist": "Ce produit a été supprimé de votre liste de souhaits",
|
"This product has been removed from your wishlist": "Ce produit a été supprimé de votre liste de souhaits",
|
||||||
"This product has been added to your wishlist": "Ce produit a été ajouté à votre liste de souhaits",
|
"This product has been added to your wishlist": "Ce produit a été ajouté à votre liste de souhaits",
|
||||||
"Spend a minimum of minimumAmount to redeem": "Dépensez un minimum de {{minimumAmount}} de racheter",
|
"Spend a minimum of minimumAmount to redeem": "Dépensez un minimum de {{minimumAmount}} de racheter",
|
||||||
"Spend less than maximumAmount to redeem": "Dépensez moins de {{maximumAmount}} de racheter"
|
"Spend less than maximumAmount to redeem": "Dépensez moins de {{maximumAmount}} de racheter",
|
||||||
|
"Related products": "Produits connexes",
|
||||||
|
"Reviews": "Commentaires",
|
||||||
|
"There are no reviews yet.": "Il n'y a pas encore de critiques.",
|
||||||
|
"More": "Suite",
|
||||||
|
"You may also like": "Tu pourrais aussi aimer",
|
||||||
|
"Leave a review": "Laisser un commentaire",
|
||||||
|
"How would you rate": "Comment évalueriez-vous",
|
||||||
|
"Submit": "Soumettre",
|
||||||
|
"Your review has been submitted": "Votre avis a été soumis"
|
||||||
}
|
}
|
||||||
@ -210,5 +210,14 @@
|
|||||||
"This product has been removed from your wishlist": "yah utpaad aapakee ichchha soochee se hata diya gaya hai",
|
"This product has been removed from your wishlist": "yah utpaad aapakee ichchha soochee se hata diya gaya hai",
|
||||||
"This product has been added to your wishlist": "is utpaad ko aapakee vish - list mein jod diya gaya hai",
|
"This product has been added to your wishlist": "is utpaad ko aapakee vish - list mein jod diya gaya hai",
|
||||||
"Spend a minimum of minimumAmount to redeem": "kam se kam kharch karen {{minimumAmount}} ke evaj mein lena",
|
"Spend a minimum of minimumAmount to redeem": "kam se kam kharch karen {{minimumAmount}} ke evaj mein lena",
|
||||||
"Spend less than maximumAmount to redeem": "se kam kharch karen {{maximumAmount}} ke evaj mein lena"
|
"Spend less than maximumAmount to redeem": "se kam kharch karen {{maximumAmount}} ke evaj mein lena",
|
||||||
|
"Related products": "sambandhit utpaad",
|
||||||
|
"Reviews": "sameeksha",
|
||||||
|
"There are no reviews yet.": "abhee tak koee sameeksha nahin.",
|
||||||
|
"More": "adhik",
|
||||||
|
"You may also like": "aapako yah bhee pasand aa sakata hain",
|
||||||
|
"Leave a review": "sameeksha likhen",
|
||||||
|
"How would you rate": "aap ise kya ret karate hain",
|
||||||
|
"Submit": "prastut karana",
|
||||||
|
"Your review has been submitted": "aapakee sameeksha jama ho chukee hai"
|
||||||
}
|
}
|
||||||
@ -210,5 +210,14 @@
|
|||||||
"This product has been removed from your wishlist": "Questo prodotto è stato rimosso dalla tua lista dei desideri",
|
"This product has been removed from your wishlist": "Questo prodotto è stato rimosso dalla tua lista dei desideri",
|
||||||
"This product has been added to your wishlist": "Questo prodotto è stato aggiunto alla tua lista dei desideri",
|
"This product has been added to your wishlist": "Questo prodotto è stato aggiunto alla tua lista dei desideri",
|
||||||
"Spend a minimum of minimumAmount to redeem": "Spendi un minimo di {{minimumAmount}} riscattare",
|
"Spend a minimum of minimumAmount to redeem": "Spendi un minimo di {{minimumAmount}} riscattare",
|
||||||
"Spend less than maximumAmount to redeem": "Spendi meno di {{maximumAmount}} riscattare"
|
"Spend less than maximumAmount to redeem": "Spendi meno di {{maximumAmount}} riscattare",
|
||||||
|
"Related products": "Prodotti correlati",
|
||||||
|
"Reviews": "Recensioni",
|
||||||
|
"There are no reviews yet.": "Non ci sono ancora recensioni.",
|
||||||
|
"More": "Di più",
|
||||||
|
"You may also like": "Potrebbe piacerti anche",
|
||||||
|
"Leave a review": "Lascia una recensione",
|
||||||
|
"How would you rate": "Come valuteresti",
|
||||||
|
"Submit": "Invia",
|
||||||
|
"Your review has been submitted": "La tua recensione è stata inviata"
|
||||||
}
|
}
|
||||||
@ -210,5 +210,14 @@
|
|||||||
"This product has been removed from your wishlist": "Dit product is van je verlanglijst verwijderd",
|
"This product has been removed from your wishlist": "Dit product is van je verlanglijst verwijderd",
|
||||||
"This product has been added to your wishlist": "Dit product is toegevoegd aan je verlanglijst",
|
"This product has been added to your wishlist": "Dit product is toegevoegd aan je verlanglijst",
|
||||||
"Spend a minimum of minimumAmount to redeem": "Besteed minimaal € {{minimumAmount}} verlossen",
|
"Spend a minimum of minimumAmount to redeem": "Besteed minimaal € {{minimumAmount}} verlossen",
|
||||||
"Spend less than maximumAmount to redeem": "Minder uitgeven dan {{maximumAmount}} verlossen"
|
"Spend less than maximumAmount to redeem": "Minder uitgeven dan {{maximumAmount}} verlossen",
|
||||||
|
"Related products": "Gerelateerde producten",
|
||||||
|
"Reviews": "Beoordelingen",
|
||||||
|
"There are no reviews yet.": "Er zijn nog geen beoordelingen.",
|
||||||
|
"More": "Meer",
|
||||||
|
"You may also like": "Dit vind je misschien ook leuk",
|
||||||
|
"Leave a review": "Laat je mening achter",
|
||||||
|
"How would you rate": "Hoe zou jij beoordelen",
|
||||||
|
"Submit": "Indienen",
|
||||||
|
"Your review has been submitted": "Uw beoordeling is verzonden"
|
||||||
}
|
}
|
||||||
@ -210,5 +210,14 @@
|
|||||||
"This product has been removed from your wishlist": "Este produto foi removido da sua lista de desejos",
|
"This product has been removed from your wishlist": "Este produto foi removido da sua lista de desejos",
|
||||||
"This product has been added to your wishlist": "Este produto foi adicionado à sua lista de desejos",
|
"This product has been added to your wishlist": "Este produto foi adicionado à sua lista de desejos",
|
||||||
"Spend a minimum of minimumAmount to redeem": "Gaste um mínimo de {{minimumAmount}} redimir",
|
"Spend a minimum of minimumAmount to redeem": "Gaste um mínimo de {{minimumAmount}} redimir",
|
||||||
"Spend less than maximumAmount to redeem": "Gaste menos que {{maximumAmount}} redimir"
|
"Spend less than maximumAmount to redeem": "Gaste menos que {{maximumAmount}} redimir",
|
||||||
|
"Related products": "Produtos relacionados",
|
||||||
|
"Reviews": "Avaliações",
|
||||||
|
"There are no reviews yet.": "Não há comentários ainda.",
|
||||||
|
"More": "Mais",
|
||||||
|
"You may also like": "você pode gostar",
|
||||||
|
"Leave a review": "Deixe um comentário",
|
||||||
|
"How would you rate": "Como você avaliaria",
|
||||||
|
"Submit": "Enviar",
|
||||||
|
"Your review has been submitted": "Sua avaliação foi enviada"
|
||||||
}
|
}
|
||||||
@ -210,5 +210,14 @@
|
|||||||
"This product has been removed from your wishlist": "Bu ürün istek listenizden kaldırıldı",
|
"This product has been removed from your wishlist": "Bu ürün istek listenizden kaldırıldı",
|
||||||
"This product has been added to your wishlist": "Bu ürün istek listenize eklendi",
|
"This product has been added to your wishlist": "Bu ürün istek listenize eklendi",
|
||||||
"Spend a minimum of minimumAmount to redeem": "Minimum harcamak {{minimumAmount}} rehinden kurtarmak",
|
"Spend a minimum of minimumAmount to redeem": "Minimum harcamak {{minimumAmount}} rehinden kurtarmak",
|
||||||
"Spend less than maximumAmount to redeem": "Şundan daha az harcayın: {{maximumAmount}} rehinden kurtarmak"
|
"Spend less than maximumAmount to redeem": "Şundan daha az harcayın: {{maximumAmount}} rehinden kurtarmak",
|
||||||
|
"Related products": "İlgili ürünler",
|
||||||
|
"Reviews": "incelemeler",
|
||||||
|
"There are no reviews yet.": "Henüz yorum yok.",
|
||||||
|
"More": "Daha",
|
||||||
|
"You may also like": "Şunlar da hoşunuza gidebilir",
|
||||||
|
"Leave a review": "İnceleme bırak",
|
||||||
|
"How would you rate": "Nasıl değerlendirirsiniz",
|
||||||
|
"Submit": "Göndermek",
|
||||||
|
"Your review has been submitted": "İncelemeniz gönderildi"
|
||||||
}
|
}
|
||||||
@ -210,5 +210,14 @@
|
|||||||
"This product has been removed from your wishlist": "该产品已从您的愿望清单中删除",
|
"This product has been removed from your wishlist": "该产品已从您的愿望清单中删除",
|
||||||
"This product has been added to your wishlist": "本产品已经被加入你的心愿单",
|
"This product has been added to your wishlist": "本产品已经被加入你的心愿单",
|
||||||
"Spend a minimum of minimumAmount to redeem": "至少花费 {{minimumAmount}} 赎回",
|
"Spend a minimum of minimumAmount to redeem": "至少花费 {{minimumAmount}} 赎回",
|
||||||
"Spend less than maximumAmount to redeem": "花费少于 {{maximumAmount}} 赎回"
|
"Spend less than maximumAmount to redeem": "花费少于 {{maximumAmount}} 赎回",
|
||||||
|
"Related products": "相关产品",
|
||||||
|
"Reviews": "评论",
|
||||||
|
"There are no reviews yet.": "还没有评论。",
|
||||||
|
"More": "更多的",
|
||||||
|
"You may also like": "你可能也会喜欢",
|
||||||
|
"Leave a review": "发表评论",
|
||||||
|
"How would you rate": "你如何评价",
|
||||||
|
"Submit": "提交",
|
||||||
|
"Your review has been submitted": "您的评论已提交"
|
||||||
}
|
}
|
||||||
@ -0,0 +1,21 @@
|
|||||||
|
// Label StoreMax
|
||||||
|
//
|
||||||
|
// Created by Anthony Gordon.
|
||||||
|
// 2022, WooSignal Ltd. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
|
||||||
|
import 'controller.dart';
|
||||||
|
import 'package:flutter/widgets.dart';
|
||||||
|
|
||||||
|
class LeaveReviewController extends Controller {
|
||||||
|
|
||||||
|
@override
|
||||||
|
construct(BuildContext context) {
|
||||||
|
super.construct(context);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -8,27 +8,110 @@
|
|||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_app/app/models/cart.dart';
|
||||||
|
import 'package:flutter_app/app/models/cart_line_item.dart';
|
||||||
|
import 'package:flutter_app/bootstrap/enums/wishlist_action_enums.dart';
|
||||||
import 'package:flutter_app/bootstrap/helpers.dart';
|
import 'package:flutter_app/bootstrap/helpers.dart';
|
||||||
|
import 'package:nylo_framework/nylo_framework.dart';
|
||||||
import 'package:woosignal/models/response/products.dart';
|
import 'package:woosignal/models/response/products.dart';
|
||||||
|
import 'package:woosignal/models/response/product_variation.dart'
|
||||||
|
as ws_product_variation;
|
||||||
|
|
||||||
import 'controller.dart';
|
import 'controller.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
|
||||||
|
|
||||||
class ProductDetailController extends Controller {
|
class ProductDetailController extends Controller {
|
||||||
|
int quantity = 1;
|
||||||
|
Product product;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
construct(BuildContext context) {
|
construct(BuildContext context) {
|
||||||
super.construct(context);
|
super.construct(context);
|
||||||
|
product = data() as Product;
|
||||||
}
|
}
|
||||||
|
|
||||||
viewExternalProduct(Product product) {
|
viewExternalProduct() {
|
||||||
if (product.externalUrl != null && product.externalUrl.isNotEmpty) {
|
if (product.externalUrl != null && product.externalUrl.isNotEmpty) {
|
||||||
openBrowserTab(url: product.externalUrl);
|
openBrowserTab(url: product.externalUrl);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
viewProductImages(int i, Product product) =>
|
itemAddToCart({@required CartLineItem cartLineItem, @required Function onSuccess}) async {
|
||||||
Navigator.pushNamed(context, "/product-images", arguments: {
|
await Cart.getInstance.addToCart(cartLineItem: cartLineItem);
|
||||||
"index": i,
|
showStatusAlert(context,
|
||||||
"images": product.images.map((f) => f.src).toList()
|
title: trans("Success"),
|
||||||
});
|
subtitle: trans("Added to cart"),
|
||||||
}
|
duration: 1,
|
||||||
|
icon: Icons.add_shopping_cart,
|
||||||
|
);
|
||||||
|
onSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
addQuantityTapped({@required Function onSuccess}) {
|
||||||
|
if (product.manageStock != null && product.manageStock == true) {
|
||||||
|
if (quantity >= product.stockQuantity) {
|
||||||
|
showToastNotification(context,
|
||||||
|
title: trans("Maximum quantity reached"),
|
||||||
|
description:
|
||||||
|
"${trans("Sorry, only")} ${product.stockQuantity} ${trans("left")}",
|
||||||
|
style: ToastNotificationStyleType.INFO);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (quantity != 0) {
|
||||||
|
quantity++;
|
||||||
|
onSuccess();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
removeQuantityTapped({@required Function onSuccess}) {
|
||||||
|
if ((quantity - 1) >= 1) {
|
||||||
|
quantity--;
|
||||||
|
onSuccess();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleWishList({@required Function onSuccess, @required WishlistAction wishlistAction}) async {
|
||||||
|
String subtitleMsg;
|
||||||
|
if (wishlistAction == WishlistAction.remove) {
|
||||||
|
await removeWishlistProduct(product: product);
|
||||||
|
subtitleMsg = trans("This product has been removed from your wishlist");
|
||||||
|
} else {
|
||||||
|
await saveWishlistProduct(product: product);
|
||||||
|
subtitleMsg = trans("This product has been added to your wishlist");
|
||||||
|
}
|
||||||
|
showStatusAlert(context,
|
||||||
|
title: trans("Success"),
|
||||||
|
subtitle: subtitleMsg,
|
||||||
|
icon: Icons.favorite,
|
||||||
|
duration: 1,
|
||||||
|
);
|
||||||
|
|
||||||
|
onSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
ws_product_variation.ProductVariation findProductVariation(
|
||||||
|
{@required Map<int, dynamic> tmpAttributeObj,
|
||||||
|
@required List<ws_product_variation.ProductVariation> productVariations}) {
|
||||||
|
ws_product_variation.ProductVariation tmpProductVariation;
|
||||||
|
|
||||||
|
Map<String, dynamic> tmpSelectedObj = {};
|
||||||
|
for (var attributeObj in tmpAttributeObj.values) {
|
||||||
|
tmpSelectedObj[attributeObj["name"]] = attributeObj["value"];
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var productVariation in productVariations) {
|
||||||
|
Map<String, dynamic> tmpVariations = {};
|
||||||
|
|
||||||
|
for (var attr in productVariation.attributes) {
|
||||||
|
tmpVariations[attr.name] = attr.option;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tmpVariations.toString() == tmpSelectedObj.toString()) {
|
||||||
|
tmpProductVariation = productVariation;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return tmpProductVariation;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -18,6 +18,7 @@ class ProductLoaderController extends WooSignalApiLoaderController<Product> {
|
|||||||
Future<void> loadProducts({
|
Future<void> loadProducts({
|
||||||
@required bool Function(bool hasProducts) hasResults,
|
@required bool Function(bool hasProducts) hasResults,
|
||||||
@required void Function() didFinish,
|
@required void Function() didFinish,
|
||||||
|
List<int> productIds = const []
|
||||||
}) async {
|
}) async {
|
||||||
await load(
|
await load(
|
||||||
hasResults: hasResults,
|
hasResults: hasResults,
|
||||||
@ -25,6 +26,7 @@ class ProductLoaderController extends WooSignalApiLoaderController<Product> {
|
|||||||
apiQuery: (api) => api.getProducts(
|
apiQuery: (api) => api.getProducts(
|
||||||
perPage: 50,
|
perPage: 50,
|
||||||
page: page,
|
page: page,
|
||||||
|
include: productIds,
|
||||||
status: "publish",
|
status: "publish",
|
||||||
stockStatus: "instock",
|
stockStatus: "instock",
|
||||||
));
|
));
|
||||||
|
|||||||
@ -0,0 +1,22 @@
|
|||||||
|
// Label StoreMax
|
||||||
|
//
|
||||||
|
// Created by Anthony Gordon.
|
||||||
|
// 2022, WooSignal Ltd. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
|
||||||
|
import 'controller.dart';
|
||||||
|
import 'package:flutter/widgets.dart';
|
||||||
|
|
||||||
|
class ProductReviewsController extends Controller {
|
||||||
|
|
||||||
|
@override
|
||||||
|
construct(BuildContext context) {
|
||||||
|
super.construct(context);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,34 @@
|
|||||||
|
// Label StoreMax
|
||||||
|
//
|
||||||
|
// Created by Anthony Gordon.
|
||||||
|
// 2022, WooSignal Ltd. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
|
||||||
|
import 'package:flutter/cupertino.dart';
|
||||||
|
import 'package:flutter_app/app/controllers/woosignal_api_loader_controller.dart';
|
||||||
|
import 'package:woosignal/models/response/product_review.dart';
|
||||||
|
import 'package:woosignal/models/response/products.dart';
|
||||||
|
|
||||||
|
class ProductReviewsLoaderController extends WooSignalApiLoaderController<ProductReview> {
|
||||||
|
ProductReviewsLoaderController();
|
||||||
|
|
||||||
|
Future<void> loadProductReviews({
|
||||||
|
@required Product product,
|
||||||
|
@required bool Function(bool hasProducts) hasResults,
|
||||||
|
@required void Function() didFinish,
|
||||||
|
}) async {
|
||||||
|
await load(
|
||||||
|
hasResults: hasResults,
|
||||||
|
didFinish: didFinish,
|
||||||
|
apiQuery: (api) => api.getProductReviews(
|
||||||
|
product: [product.id],
|
||||||
|
perPage: 50,
|
||||||
|
page: page,
|
||||||
|
status: "approved",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
15
LabelStoreMax/lib/bootstrap/enums/wishlist_action_enums.dart
Normal file
15
LabelStoreMax/lib/bootstrap/enums/wishlist_action_enums.dart
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
// Label StoreMax
|
||||||
|
//
|
||||||
|
// Created by Anthony Gordon.
|
||||||
|
// 2022, WooSignal Ltd. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
|
||||||
|
|
||||||
|
enum WishlistAction {
|
||||||
|
add,
|
||||||
|
remove
|
||||||
|
}
|
||||||
@ -99,7 +99,7 @@ List<PaymentType> getPaymentTypes() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dynamic envVal(String envVal, {dynamic defaultValue}) =>
|
dynamic envVal(String envVal, {dynamic defaultValue}) =>
|
||||||
(getEnv(envVal) == null ? defaultValue : getEnv(envVal));
|
(getEnv(envVal) ?? defaultValue);
|
||||||
|
|
||||||
PaymentType addPayment(
|
PaymentType addPayment(
|
||||||
{@required int id,
|
{@required int id,
|
||||||
@ -372,7 +372,7 @@ navigatorPush(BuildContext context,
|
|||||||
if (forgetAll) {
|
if (forgetAll) {
|
||||||
Navigator.of(context).pushNamedAndRemoveUntil(
|
Navigator.of(context).pushNamedAndRemoveUntil(
|
||||||
routeName, (Route<dynamic> route) => false,
|
routeName, (Route<dynamic> route) => false,
|
||||||
arguments: arguments ?? null);
|
arguments: arguments);
|
||||||
}
|
}
|
||||||
if (forgetLast != null) {
|
if (forgetLast != null) {
|
||||||
int count = 0;
|
int count = 0;
|
||||||
@ -380,7 +380,7 @@ navigatorPush(BuildContext context,
|
|||||||
return count++ == forgetLast;
|
return count++ == forgetLast;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
Navigator.of(context).pushNamed(routeName, arguments: arguments ?? null);
|
Navigator.of(context).pushNamed(routeName, arguments: arguments);
|
||||||
}
|
}
|
||||||
|
|
||||||
PlatformDialogAction dialogAction(BuildContext context,
|
PlatformDialogAction dialogAction(BuildContext context,
|
||||||
@ -554,6 +554,16 @@ Future<List<dynamic>> getWishlistProducts() async {
|
|||||||
return favouriteProducts;
|
return favouriteProducts;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hasAddedWishlistProduct(int productId) async {
|
||||||
|
List<dynamic> favouriteProducts = await getWishlistProducts();
|
||||||
|
List<int> productIds =
|
||||||
|
favouriteProducts.map((e) => e['id']).cast<int>().toList();
|
||||||
|
if (productIds.isEmpty) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return productIds.contains(productId);
|
||||||
|
}
|
||||||
|
|
||||||
saveWishlistProduct({@required Product product}) async {
|
saveWishlistProduct({@required Product product}) async {
|
||||||
List<dynamic> products = await getWishlistProducts();
|
List<dynamic> products = await getWishlistProducts();
|
||||||
if (products.any((wishListProduct) => wishListProduct['id'] == product.id) ==
|
if (products.any((wishListProduct) => wishListProduct['id'] == product.id) ==
|
||||||
|
|||||||
@ -17,7 +17,6 @@ import 'package:flutter_app/bootstrap/shared_pref/sp_auth.dart';
|
|||||||
import 'package:flutter_app/resources/widgets/app_loader_widget.dart';
|
import 'package:flutter_app/resources/widgets/app_loader_widget.dart';
|
||||||
import 'package:flutter_app/resources/widgets/safearea_widget.dart';
|
import 'package:flutter_app/resources/widgets/safearea_widget.dart';
|
||||||
import 'package:flutter_app/resources/widgets/woosignal_ui.dart';
|
import 'package:flutter_app/resources/widgets/woosignal_ui.dart';
|
||||||
import 'package:hexcolor/hexcolor.dart';
|
|
||||||
import 'package:nylo_framework/nylo_framework.dart';
|
import 'package:nylo_framework/nylo_framework.dart';
|
||||||
import 'package:pull_to_refresh/pull_to_refresh.dart';
|
import 'package:pull_to_refresh/pull_to_refresh.dart';
|
||||||
import 'package:woosignal/models/response/order.dart';
|
import 'package:woosignal/models/response/order.dart';
|
||||||
@ -343,7 +342,7 @@ class _AccountDetailPageState extends State<AccountDetailPage>
|
|||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
border: Border(
|
border: Border(
|
||||||
bottom: BorderSide(
|
bottom: BorderSide(
|
||||||
color: HexColor("#fcfcfc"),
|
color: Color(0xFFFCFCFC),
|
||||||
width: 1,
|
width: 1,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@ -14,7 +14,6 @@ import 'package:flutter_app/bootstrap/helpers.dart';
|
|||||||
import 'package:flutter_app/resources/widgets/app_loader_widget.dart';
|
import 'package:flutter_app/resources/widgets/app_loader_widget.dart';
|
||||||
import 'package:flutter_app/resources/widgets/safearea_widget.dart';
|
import 'package:flutter_app/resources/widgets/safearea_widget.dart';
|
||||||
import 'package:flutter_app/resources/widgets/woosignal_ui.dart';
|
import 'package:flutter_app/resources/widgets/woosignal_ui.dart';
|
||||||
import 'package:hexcolor/hexcolor.dart';
|
|
||||||
import 'package:nylo_support/helpers/helper.dart';
|
import 'package:nylo_support/helpers/helper.dart';
|
||||||
import 'package:nylo_support/widgets/ny_state.dart';
|
import 'package:nylo_support/widgets/ny_state.dart';
|
||||||
import 'package:nylo_support/widgets/ny_stateful_widget.dart';
|
import 'package:nylo_support/widgets/ny_stateful_widget.dart';
|
||||||
@ -63,12 +62,15 @@ class _AccountOrderDetailPageState extends NyState<AccountOrderDetailPage> {
|
|||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Text(
|
Padding(
|
||||||
"${trans("Date Ordered").capitalize()}: " +
|
padding: EdgeInsets.only(top: 8),
|
||||||
dateFormatted(
|
child: Text(
|
||||||
date: _order.dateCreated,
|
"${trans("Date Ordered").capitalize()}: " +
|
||||||
formatType: formatForDateTime(FormatType.date),
|
dateFormatted(
|
||||||
),
|
date: _order.dateCreated,
|
||||||
|
formatType: formatForDateTime(FormatType.date),
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
Container(
|
Container(
|
||||||
margin: EdgeInsets.only(top: 10, bottom: 10),
|
margin: EdgeInsets.only(top: 10, bottom: 10),
|
||||||
@ -116,6 +118,7 @@ class _AccountOrderDetailPageState extends NyState<AccountOrderDetailPage> {
|
|||||||
Expanded(
|
Expanded(
|
||||||
child: ListView.builder(
|
child: ListView.builder(
|
||||||
itemBuilder: (cxt, i) {
|
itemBuilder: (cxt, i) {
|
||||||
|
LineItems lineItem = _order.lineItems[i];
|
||||||
return Card(
|
return Card(
|
||||||
child: ListTile(
|
child: ListTile(
|
||||||
contentPadding: EdgeInsets.only(
|
contentPadding: EdgeInsets.only(
|
||||||
@ -124,7 +127,7 @@ class _AccountOrderDetailPageState extends NyState<AccountOrderDetailPage> {
|
|||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
border: Border(
|
border: Border(
|
||||||
bottom: BorderSide(
|
bottom: BorderSide(
|
||||||
color: HexColor("#fcfcfc"), width: 1),
|
color: Color(0xFFFCFCFC), width: 1),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
child: Row(
|
child: Row(
|
||||||
@ -134,14 +137,13 @@ class _AccountOrderDetailPageState extends NyState<AccountOrderDetailPage> {
|
|||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Flexible(
|
Flexible(
|
||||||
child: Text(
|
child: Text(
|
||||||
_order.lineItems[i].name,
|
lineItem.name,
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
formatStringCurrency(
|
formatStringCurrency(total: lineItem.price)
|
||||||
total: _order.lineItems[i].price)
|
|
||||||
.capitalize(),
|
.capitalize(),
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
@ -163,7 +165,7 @@ class _AccountOrderDetailPageState extends NyState<AccountOrderDetailPage> {
|
|||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Text(
|
Text(
|
||||||
formatStringCurrency(
|
formatStringCurrency(
|
||||||
total: _order.lineItems[i].total,
|
total: lineItem.total,
|
||||||
),
|
),
|
||||||
style: Theme.of(context)
|
style: Theme.of(context)
|
||||||
.textTheme
|
.textTheme
|
||||||
@ -174,7 +176,7 @@ class _AccountOrderDetailPageState extends NyState<AccountOrderDetailPage> {
|
|||||||
textAlign: TextAlign.left,
|
textAlign: TextAlign.left,
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
"x${_order.lineItems[i].quantity.toString()}",
|
"x${lineItem.quantity.toString()}",
|
||||||
style: Theme.of(context)
|
style: Theme.of(context)
|
||||||
.textTheme
|
.textTheme
|
||||||
.bodyText1,
|
.bodyText1,
|
||||||
|
|||||||
@ -131,7 +131,7 @@ class _CustomerCountriesPageState extends State<CustomerCountriesPage> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_handleCountryTapped(DefaultShipping defaultShipping) {
|
_handleCountryTapped(DefaultShipping defaultShipping) {
|
||||||
if (defaultShipping.states.length > 0) {
|
if (defaultShipping.states.isNotEmpty) {
|
||||||
_handleStates(defaultShipping);
|
_handleStates(defaultShipping);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
193
LabelStoreMax/lib/resources/pages/leave_review_page.dart
Normal file
193
LabelStoreMax/lib/resources/pages/leave_review_page.dart
Normal file
@ -0,0 +1,193 @@
|
|||||||
|
// Label StoreMax
|
||||||
|
//
|
||||||
|
// Created by Anthony Gordon.
|
||||||
|
// 2022, WooSignal Ltd. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_app/bootstrap/helpers.dart';
|
||||||
|
import 'package:flutter_app/bootstrap/shared_pref/sp_auth.dart';
|
||||||
|
import 'package:flutter_app/resources/widgets/app_loader_widget.dart';
|
||||||
|
import 'package:flutter_app/resources/widgets/buttons.dart';
|
||||||
|
import 'package:flutter_app/resources/widgets/safearea_widget.dart';
|
||||||
|
import 'package:flutter_rating_bar/flutter_rating_bar.dart';
|
||||||
|
import 'package:nylo_framework/nylo_framework.dart';
|
||||||
|
import 'package:woosignal/models/response/order.dart';
|
||||||
|
import 'package:woosignal/models/response/product_review.dart';
|
||||||
|
import 'package:wp_json_api/models/responses/wc_customer_info_response.dart'
|
||||||
|
as wc_customer_info;
|
||||||
|
import 'package:wp_json_api/wp_json_api.dart';
|
||||||
|
import '../../app/controllers/leave_review_controller.dart';
|
||||||
|
|
||||||
|
class LeaveReviewPage extends NyStatefulWidget {
|
||||||
|
final LeaveReviewController controller = LeaveReviewController();
|
||||||
|
|
||||||
|
LeaveReviewPage({Key key}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
_LeaveReviewPageState createState() => _LeaveReviewPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _LeaveReviewPageState extends NyState<LeaveReviewPage> {
|
||||||
|
LineItems _lineItem;
|
||||||
|
Order _order;
|
||||||
|
|
||||||
|
TextEditingController _textEditingController;
|
||||||
|
int _rating;
|
||||||
|
bool _isLoading = false;
|
||||||
|
|
||||||
|
@override
|
||||||
|
widgetDidLoad() async {
|
||||||
|
_lineItem = widget.controller.data()['line_item'] as LineItems;
|
||||||
|
_order = widget.controller.data()['order'] as Order;
|
||||||
|
_textEditingController = TextEditingController();
|
||||||
|
_rating = 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title: Text(trans("Leave a review")),
|
||||||
|
centerTitle: true,
|
||||||
|
),
|
||||||
|
body: SafeAreaWidget(
|
||||||
|
child: _isLoading
|
||||||
|
? AppLoaderWidget()
|
||||||
|
: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Padding(
|
||||||
|
padding: EdgeInsets.only(top: 16),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
trans("How would you rate"),
|
||||||
|
style: Theme.of(context).textTheme.bodyText1,
|
||||||
|
),
|
||||||
|
Text(_lineItem.name),
|
||||||
|
Flexible(
|
||||||
|
child: Container(
|
||||||
|
child: TextField(
|
||||||
|
controller: _textEditingController,
|
||||||
|
style: Theme.of(context).textTheme.subtitle1,
|
||||||
|
keyboardType: TextInputType.text,
|
||||||
|
autocorrect: false,
|
||||||
|
autofocus: true,
|
||||||
|
obscureText: false,
|
||||||
|
textCapitalization: TextCapitalization.sentences,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: EdgeInsets.only(bottom: 16),
|
||||||
|
),
|
||||||
|
RatingBar.builder(
|
||||||
|
initialRating: _rating.toDouble(),
|
||||||
|
minRating: 1,
|
||||||
|
direction: Axis.horizontal,
|
||||||
|
allowHalfRating: false,
|
||||||
|
itemCount: 5,
|
||||||
|
itemPadding: EdgeInsets.symmetric(horizontal: 4.0),
|
||||||
|
itemBuilder: (context, _) =>
|
||||||
|
Icon(Icons.star, color: Colors.amber),
|
||||||
|
onRatingUpdate: (rating) {
|
||||||
|
_rating = rating.toInt();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: EdgeInsets.only(bottom: 16),
|
||||||
|
),
|
||||||
|
PrimaryButton(title: trans("Submit"), action: _leaveReview),
|
||||||
|
],
|
||||||
|
)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
_leaveReview() async {
|
||||||
|
if (_isLoading == true) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setState(() {
|
||||||
|
_isLoading = true;
|
||||||
|
});
|
||||||
|
String review = _textEditingController.text;
|
||||||
|
wc_customer_info.Data wcCustomerInfo = await _fetchWpUserData();
|
||||||
|
if (wcCustomerInfo == null) {
|
||||||
|
showToastNotification(
|
||||||
|
context,
|
||||||
|
title: trans("Oops!"),
|
||||||
|
description: trans("Something went wrong"),
|
||||||
|
style: ToastNotificationStyleType.DANGER,
|
||||||
|
);
|
||||||
|
setState(() {
|
||||||
|
_isLoading = false;
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
validator(rules: {"review": "min:5"}, data: {"review": review});
|
||||||
|
|
||||||
|
ProductReview productReview =
|
||||||
|
await appWooSignal((api) => api.createProductReview(
|
||||||
|
productId: _lineItem.productId,
|
||||||
|
verified: true,
|
||||||
|
review: review,
|
||||||
|
status: "approved",
|
||||||
|
reviewer: [_order.billing.firstName, _order.billing.lastName]
|
||||||
|
.join(" "),
|
||||||
|
rating: _rating,
|
||||||
|
reviewerEmail: _order.billing.email,
|
||||||
|
));
|
||||||
|
|
||||||
|
if (productReview == null) {
|
||||||
|
showToastNotification(context,
|
||||||
|
title: trans("Oops"),
|
||||||
|
description: trans("Something went wrong"),
|
||||||
|
style: ToastNotificationStyleType.INFO);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
showToastNotification(context,
|
||||||
|
title: trans("Success"),
|
||||||
|
description: trans("Your review has been submitted"),
|
||||||
|
style: ToastNotificationStyleType.SUCCESS);
|
||||||
|
pop(result: _lineItem);
|
||||||
|
} on ValidationException catch (e) {
|
||||||
|
NyLogger.error(e.toString());
|
||||||
|
} finally {
|
||||||
|
setState(() {
|
||||||
|
_isLoading = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<wc_customer_info.Data> _fetchWpUserData() async {
|
||||||
|
String userToken = await readAuthToken();
|
||||||
|
|
||||||
|
wc_customer_info.WCCustomerInfoResponse wcCustomerInfoResponse;
|
||||||
|
try {
|
||||||
|
wcCustomerInfoResponse = await WPJsonAPI.instance
|
||||||
|
.api((request) => request.wcCustomerInfo(userToken));
|
||||||
|
|
||||||
|
if (wcCustomerInfoResponse == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (wcCustomerInfoResponse.status != 200) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return wcCustomerInfoResponse.data;
|
||||||
|
} on Exception catch (_) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -10,20 +10,21 @@
|
|||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_app/app/controllers/product_detail_controller.dart';
|
import 'package:flutter_app/app/controllers/product_detail_controller.dart';
|
||||||
import 'package:flutter_app/app/models/cart.dart';
|
|
||||||
import 'package:flutter_app/app/models/cart_line_item.dart';
|
import 'package:flutter_app/app/models/cart_line_item.dart';
|
||||||
import 'package:flutter_app/bootstrap/app_helper.dart';
|
import 'package:flutter_app/bootstrap/app_helper.dart';
|
||||||
|
import 'package:flutter_app/bootstrap/enums/wishlist_action_enums.dart';
|
||||||
import 'package:flutter_app/bootstrap/helpers.dart';
|
import 'package:flutter_app/bootstrap/helpers.dart';
|
||||||
import 'package:flutter_app/resources/widgets/app_loader_widget.dart';
|
import 'package:flutter_app/resources/widgets/app_loader_widget.dart';
|
||||||
import 'package:flutter_app/resources/widgets/buttons.dart';
|
import 'package:flutter_app/resources/widgets/buttons.dart';
|
||||||
import 'package:flutter_app/resources/widgets/cached_image_widget.dart';
|
|
||||||
import 'package:flutter_app/resources/widgets/cart_icon_widget.dart';
|
import 'package:flutter_app/resources/widgets/cart_icon_widget.dart';
|
||||||
|
import 'package:flutter_app/resources/widgets/future_build_widget.dart';
|
||||||
|
import 'package:flutter_app/resources/widgets/product_detail_body_widget.dart';
|
||||||
|
import 'package:flutter_app/resources/widgets/product_detail_footer_actions_widget.dart';
|
||||||
import 'package:flutter_app/resources/widgets/woosignal_ui.dart';
|
import 'package:flutter_app/resources/widgets/woosignal_ui.dart';
|
||||||
import 'package:nylo_framework/nylo_framework.dart';
|
import 'package:nylo_framework/nylo_framework.dart';
|
||||||
import 'package:woosignal/models/response/product_variation.dart'
|
import 'package:woosignal/models/response/product_variation.dart'
|
||||||
as ws_product_variation;
|
as ws_product_variation;
|
||||||
import 'package:woosignal/models/response/products.dart' as ws_product;
|
import 'package:woosignal/models/response/products.dart' as ws_product;
|
||||||
import 'package:flutter_swiper/flutter_swiper.dart';
|
|
||||||
import 'package:woosignal/models/response/woosignal_app.dart';
|
import 'package:woosignal/models/response/woosignal_app.dart';
|
||||||
|
|
||||||
class ProductDetailPage extends NyStatefulWidget {
|
class ProductDetailPage extends NyStatefulWidget {
|
||||||
@ -36,10 +37,9 @@ class ProductDetailPage extends NyStatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _ProductDetailState extends NyState<ProductDetailPage> {
|
class _ProductDetailState extends NyState<ProductDetailPage> {
|
||||||
bool _isLoading = false;
|
bool _isLoading = true;
|
||||||
ws_product.Product _product;
|
ws_product.Product _product;
|
||||||
bool isInFavourites = false;
|
|
||||||
int _quantityIndicator = 1;
|
|
||||||
List<ws_product_variation.ProductVariation> _productVariations = [];
|
List<ws_product_variation.ProductVariation> _productVariations = [];
|
||||||
final Map<int, dynamic> _tmpAttributeObj = {};
|
final Map<int, dynamic> _tmpAttributeObj = {};
|
||||||
final WooSignalApp _wooSignalApp = AppHelper.instance.appConfig;
|
final WooSignalApp _wooSignalApp = AppHelper.instance.appConfig;
|
||||||
@ -47,11 +47,13 @@ class _ProductDetailState extends NyState<ProductDetailPage> {
|
|||||||
@override
|
@override
|
||||||
widgetDidLoad() async {
|
widgetDidLoad() async {
|
||||||
_product = widget.controller.data();
|
_product = widget.controller.data();
|
||||||
|
|
||||||
if (_product.type == "variable") {
|
if (_product.type == "variable") {
|
||||||
_isLoading = true;
|
|
||||||
await _fetchProductVariations();
|
await _fetchProductVariations();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
setState(() {
|
||||||
|
_isLoading = false;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
_fetchProductVariations() async {
|
_fetchProductVariations() async {
|
||||||
@ -80,29 +82,6 @@ class _ProductDetailState extends NyState<ProductDetailPage> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
ws_product_variation.ProductVariation findProductVariation() {
|
|
||||||
ws_product_variation.ProductVariation tmpProductVariation;
|
|
||||||
|
|
||||||
Map<String, dynamic> tmpSelectedObj = {};
|
|
||||||
for (var attributeObj in _tmpAttributeObj.values) {
|
|
||||||
tmpSelectedObj[attributeObj["name"]] = attributeObj["value"];
|
|
||||||
}
|
|
||||||
|
|
||||||
for (var productVariation in _productVariations) {
|
|
||||||
Map<String, dynamic> tmpVariations = {};
|
|
||||||
|
|
||||||
for (var attr in productVariation.attributes) {
|
|
||||||
tmpVariations[attr.name] = attr.option;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tmpVariations.toString() == tmpSelectedObj.toString()) {
|
|
||||||
tmpProductVariation = productVariation;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return tmpProductVariation;
|
|
||||||
}
|
|
||||||
|
|
||||||
_modalBottomSheetOptionsForAttribute(int attributeIndex) {
|
_modalBottomSheetOptionsForAttribute(int attributeIndex) {
|
||||||
wsModalBottom(
|
wsModalBottom(
|
||||||
context,
|
context,
|
||||||
@ -137,19 +116,11 @@ class _ProductDetailState extends NyState<ProductDetailPage> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
_itemAddToCart({CartLineItem cartLineItem}) async {
|
|
||||||
await Cart.getInstance.addToCart(cartLineItem: cartLineItem);
|
|
||||||
showStatusAlert(context,
|
|
||||||
title: trans("Success"),
|
|
||||||
subtitle: trans("Added to cart"),
|
|
||||||
duration: 1,
|
|
||||||
icon: Icons.add_shopping_cart);
|
|
||||||
setState(() {});
|
|
||||||
}
|
|
||||||
|
|
||||||
_modalBottomSheetAttributes() {
|
_modalBottomSheetAttributes() {
|
||||||
ws_product_variation.ProductVariation productVariation =
|
ws_product_variation.ProductVariation productVariation = widget.controller
|
||||||
findProductVariation();
|
.findProductVariation(
|
||||||
|
tmpAttributeObj: _tmpAttributeObj,
|
||||||
|
productVariations: _productVariations);
|
||||||
wsModalBottom(
|
wsModalBottom(
|
||||||
context,
|
context,
|
||||||
title: trans("Options"),
|
title: trans("Options"),
|
||||||
@ -203,7 +174,7 @@ class _ProductDetailState extends NyState<ProductDetailPage> {
|
|||||||
),
|
),
|
||||||
PrimaryButton(
|
PrimaryButton(
|
||||||
title: trans("Add to cart"),
|
title: trans("Add to cart"),
|
||||||
action: () {
|
action: () async {
|
||||||
if (_product.attributes.length !=
|
if (_product.attributes.length !=
|
||||||
_tmpAttributeObj.values.length) {
|
_tmpAttributeObj.values.length) {
|
||||||
showToastNotification(context,
|
showToastNotification(context,
|
||||||
@ -235,12 +206,16 @@ class _ProductDetailState extends NyState<ProductDetailPage> {
|
|||||||
});
|
});
|
||||||
|
|
||||||
CartLineItem cartLineItem = CartLineItem.fromProductVariation(
|
CartLineItem cartLineItem = CartLineItem.fromProductVariation(
|
||||||
quantityAmount: _quantityIndicator,
|
quantityAmount: widget.controller.quantity,
|
||||||
options: options,
|
options: options,
|
||||||
product: _product,
|
product: _product,
|
||||||
productVariation: productVariation);
|
productVariation: productVariation,
|
||||||
|
);
|
||||||
|
|
||||||
_itemAddToCart(cartLineItem: cartLineItem);
|
await widget.controller.itemAddToCart(
|
||||||
|
cartLineItem: cartLineItem,
|
||||||
|
onSuccess: () => setState(() => {}));
|
||||||
|
setState(() {});
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
@ -250,30 +225,28 @@ class _ProductDetailState extends NyState<ProductDetailPage> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
_modalBottomSheetMenu() {
|
|
||||||
wsModalBottom(
|
|
||||||
context,
|
|
||||||
title: trans("Description"),
|
|
||||||
bodyWidget: SingleChildScrollView(
|
|
||||||
child: Text(
|
|
||||||
parseHtmlString(_product.description),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
actions: <Widget>[
|
actions: <Widget>[
|
||||||
if (_wooSignalApp.wishlistEnabled)
|
if (_wooSignalApp.wishlistEnabled)
|
||||||
IconButton(
|
FutureBuildWidget(
|
||||||
onPressed: _toggleWishList,
|
asyncFuture: hasAddedWishlistProduct(_product.id),
|
||||||
icon: isInFavourites
|
onValue: (isInFavourites) {
|
||||||
? Icon(Icons.favorite, color: Colors.red)
|
return isInFavourites
|
||||||
: Icon(Icons.favorite_border, color: Colors.black54),
|
? IconButton(
|
||||||
),
|
onPressed: () => widget.controller.toggleWishList(
|
||||||
|
onSuccess: () => setState(() {}),
|
||||||
|
wishlistAction: WishlistAction.remove),
|
||||||
|
icon: Icon(Icons.favorite, color: Colors.red))
|
||||||
|
: IconButton(
|
||||||
|
onPressed: () => widget.controller.toggleWishList(
|
||||||
|
onSuccess: () => setState(() {}),
|
||||||
|
wishlistAction: WishlistAction.add),
|
||||||
|
icon: Icon(Icons.favorite_border,
|
||||||
|
color: Colors.black54));
|
||||||
|
}),
|
||||||
CartIconWidget(),
|
CartIconWidget(),
|
||||||
],
|
],
|
||||||
title: StoreLogo(
|
title: StoreLogo(
|
||||||
@ -289,248 +262,30 @@ class _ProductDetailState extends NyState<ProductDetailPage> {
|
|||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Expanded(
|
Expanded(
|
||||||
child: ListView(
|
child: ProductDetailBodyWidget(
|
||||||
children: <Widget>[
|
wooSignalApp: _wooSignalApp,
|
||||||
SizedBox(
|
product: _product,
|
||||||
height: MediaQuery.of(context).size.height * 0.40,
|
|
||||||
child: SizedBox(
|
|
||||||
child: Swiper(
|
|
||||||
itemBuilder: (BuildContext context, int index) =>
|
|
||||||
CachedImageWidget(
|
|
||||||
image: _product.images.isNotEmpty
|
|
||||||
? _product.images[index].src
|
|
||||||
: getEnv("PRODUCT_PLACEHOLDER_IMAGE"),
|
|
||||||
),
|
|
||||||
itemCount: _product.images.isEmpty
|
|
||||||
? 1
|
|
||||||
: _product.images.length,
|
|
||||||
viewportFraction: 0.85,
|
|
||||||
scale: 0.9,
|
|
||||||
onTap: (int i) => widget.controller
|
|
||||||
.viewProductImages(i, _product),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Container(
|
|
||||||
height: 100,
|
|
||||||
padding: EdgeInsets.symmetric(
|
|
||||||
vertical: 10,
|
|
||||||
horizontal: 16,
|
|
||||||
),
|
|
||||||
child: Row(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: <Widget>[
|
|
||||||
Flexible(
|
|
||||||
child: Text(
|
|
||||||
_product.name,
|
|
||||||
style: Theme.of(context)
|
|
||||||
.textTheme
|
|
||||||
.bodyText1
|
|
||||||
.copyWith(fontSize: 20),
|
|
||||||
textAlign: TextAlign.left,
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
maxLines: 2,
|
|
||||||
),
|
|
||||||
flex: 4,
|
|
||||||
),
|
|
||||||
Flexible(
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.end,
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: <Widget>[
|
|
||||||
Text(
|
|
||||||
formatStringCurrency(
|
|
||||||
total: _product.price),
|
|
||||||
style: Theme.of(context)
|
|
||||||
.textTheme
|
|
||||||
.headline4
|
|
||||||
.copyWith(
|
|
||||||
fontSize: 20,
|
|
||||||
),
|
|
||||||
textAlign: TextAlign.right,
|
|
||||||
),
|
|
||||||
(_product.onSale == true &&
|
|
||||||
_product.type != "variable"
|
|
||||||
? Text(
|
|
||||||
formatStringCurrency(
|
|
||||||
total: _product.regularPrice),
|
|
||||||
style: TextStyle(
|
|
||||||
color: Colors.grey,
|
|
||||||
decoration:
|
|
||||||
TextDecoration.lineThrough,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
: null)
|
|
||||||
].where((t) => t != null).toList(),
|
|
||||||
),
|
|
||||||
flex: 2,
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Container(
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: ThemeColor.get(context).background,
|
|
||||||
borderRadius: BorderRadius.circular(4),
|
|
||||||
),
|
|
||||||
padding:
|
|
||||||
EdgeInsets.symmetric(vertical: 4, horizontal: 16),
|
|
||||||
height: 180,
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
|
||||||
children: <Widget>[
|
|
||||||
Row(
|
|
||||||
mainAxisAlignment:
|
|
||||||
MainAxisAlignment.spaceBetween,
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
|
||||||
children: <Widget>[
|
|
||||||
Text(
|
|
||||||
trans("Description"),
|
|
||||||
style: Theme.of(context)
|
|
||||||
.textTheme
|
|
||||||
.caption
|
|
||||||
.copyWith(fontSize: 18),
|
|
||||||
textAlign: TextAlign.left,
|
|
||||||
),
|
|
||||||
MaterialButton(
|
|
||||||
child: Text(
|
|
||||||
trans("Full description"),
|
|
||||||
style: Theme.of(context)
|
|
||||||
.textTheme
|
|
||||||
.bodyText2
|
|
||||||
.copyWith(fontSize: 14),
|
|
||||||
textAlign: TextAlign.right,
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
),
|
|
||||||
height: 50,
|
|
||||||
minWidth: 60,
|
|
||||||
onPressed: _modalBottomSheetMenu,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
Flexible(
|
|
||||||
child: Text(
|
|
||||||
(_product.shortDescription != null &&
|
|
||||||
_product.shortDescription != ""
|
|
||||||
? parseHtmlString(
|
|
||||||
_product.shortDescription)
|
|
||||||
: parseHtmlString(_product.description)),
|
|
||||||
maxLines: 5,
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
),
|
|
||||||
flex: 3,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Container(
|
// </Product body>
|
||||||
padding: EdgeInsets.symmetric(horizontal: 16),
|
ProductDetailFooterActionsWidget(
|
||||||
decoration: BoxDecoration(
|
onAddToCart: _addItemToCart,
|
||||||
color: ThemeColor.get(context).background,
|
onViewExternalProduct:
|
||||||
boxShadow: [
|
widget.controller.viewExternalProduct,
|
||||||
BoxShadow(
|
onAddQuantity: () => widget.controller
|
||||||
color: Colors.black12,
|
.addQuantityTapped(onSuccess: () => setState(() {})),
|
||||||
blurRadius: 15.0,
|
onRemoveQuantity: () => widget.controller
|
||||||
spreadRadius: -17,
|
.removeQuantityTapped(onSuccess: () => setState(() {})),
|
||||||
offset: Offset(
|
product: _product,
|
||||||
0,
|
quantity: widget.controller.quantity,
|
||||||
-10,
|
)
|
||||||
),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
|
||||||
children: <Widget>[
|
|
||||||
(_product.type != "external"
|
|
||||||
? Row(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
|
||||||
mainAxisAlignment:
|
|
||||||
MainAxisAlignment.spaceBetween,
|
|
||||||
children: <Widget>[
|
|
||||||
Text(
|
|
||||||
trans("Quantity"),
|
|
||||||
style: Theme.of(context)
|
|
||||||
.textTheme
|
|
||||||
.bodyText1
|
|
||||||
.copyWith(color: Colors.grey),
|
|
||||||
),
|
|
||||||
Row(
|
|
||||||
children: <Widget>[
|
|
||||||
IconButton(
|
|
||||||
icon: Icon(
|
|
||||||
Icons.remove_circle_outline,
|
|
||||||
size: 28,
|
|
||||||
),
|
|
||||||
onPressed: _removeQuantityTapped,
|
|
||||||
),
|
|
||||||
Text(
|
|
||||||
_quantityIndicator.toString(),
|
|
||||||
style: Theme.of(context)
|
|
||||||
.textTheme
|
|
||||||
.bodyText1,
|
|
||||||
),
|
|
||||||
IconButton(
|
|
||||||
icon: Icon(
|
|
||||||
Icons.add_circle_outline,
|
|
||||||
size: 28,
|
|
||||||
),
|
|
||||||
onPressed: _addQuantityTapped,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
],
|
|
||||||
)
|
|
||||||
: null),
|
|
||||||
Row(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: <Widget>[
|
|
||||||
Flexible(
|
|
||||||
child: Align(
|
|
||||||
child: Text(
|
|
||||||
formatStringCurrency(
|
|
||||||
total: (parseWcPrice(_product.price) *
|
|
||||||
_quantityIndicator)
|
|
||||||
.toString()),
|
|
||||||
style: Theme.of(context).textTheme.headline4,
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
),
|
|
||||||
alignment: Alignment.centerLeft,
|
|
||||||
)),
|
|
||||||
_product.type == "external"
|
|
||||||
? Flexible(
|
|
||||||
child: PrimaryButton(
|
|
||||||
title: trans("Buy Product"),
|
|
||||||
action: () => widget.controller
|
|
||||||
.viewExternalProduct(_product),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
: Flexible(
|
|
||||||
child: PrimaryButton(
|
|
||||||
title: trans("Add to cart"),
|
|
||||||
action: () => _addItemToCart(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
].where((e) => e != null).toList(),
|
|
||||||
),
|
|
||||||
height: 140,
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
_addItemToCart() {
|
_addItemToCart() async {
|
||||||
if (_product.type != "simple") {
|
if (_product.type != "simple") {
|
||||||
_modalBottomSheetAttributes();
|
_modalBottomSheetAttributes();
|
||||||
return;
|
return;
|
||||||
@ -543,53 +298,10 @@ class _ProductDetailState extends NyState<ProductDetailPage> {
|
|||||||
icon: Icons.local_shipping);
|
icon: Icons.local_shipping);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_itemAddToCart(
|
|
||||||
|
await widget.controller.itemAddToCart(
|
||||||
cartLineItem: CartLineItem.fromProduct(
|
cartLineItem: CartLineItem.fromProduct(
|
||||||
quantityAmount: _quantityIndicator, product: _product));
|
quantityAmount: widget.controller.quantity, product: _product),
|
||||||
}
|
onSuccess: () => setState(() {}));
|
||||||
|
|
||||||
_addQuantityTapped() {
|
|
||||||
if (_product.manageStock != null && _product.manageStock == true) {
|
|
||||||
if (_quantityIndicator >= _product.stockQuantity) {
|
|
||||||
showToastNotification(context,
|
|
||||||
title: trans("Maximum quantity reached"),
|
|
||||||
description:
|
|
||||||
"${trans("Sorry, only")} ${_product.stockQuantity} ${trans("left")}",
|
|
||||||
style: ToastNotificationStyleType.INFO);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (_quantityIndicator != 0) {
|
|
||||||
setState(() {
|
|
||||||
_quantityIndicator++;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_removeQuantityTapped() {
|
|
||||||
if ((_quantityIndicator - 1) >= 1) {
|
|
||||||
setState(() {
|
|
||||||
_quantityIndicator--;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_toggleWishList() async {
|
|
||||||
String subtitleMsg;
|
|
||||||
if (isInFavourites) {
|
|
||||||
await removeWishlistProduct(product: _product);
|
|
||||||
subtitleMsg = trans("This product has been removed from your wishlist");
|
|
||||||
} else {
|
|
||||||
await saveWishlistProduct(product: _product);
|
|
||||||
subtitleMsg = trans("This product has been added to your wishlist");
|
|
||||||
}
|
|
||||||
showStatusAlert(context,
|
|
||||||
title: trans("Success"),
|
|
||||||
subtitle: subtitleMsg,
|
|
||||||
icon: Icons.favorite,
|
|
||||||
duration: 1);
|
|
||||||
|
|
||||||
isInFavourites = !isInFavourites;
|
|
||||||
setState(() {});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
213
LabelStoreMax/lib/resources/pages/product_reviews_page.dart
Normal file
213
LabelStoreMax/lib/resources/pages/product_reviews_page.dart
Normal file
@ -0,0 +1,213 @@
|
|||||||
|
// Label StoreMax
|
||||||
|
//
|
||||||
|
// Created by Anthony Gordon.
|
||||||
|
// 2022, WooSignal Ltd. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
|
||||||
|
import 'package:flutter/cupertino.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_app/app/controllers/product_reviews_loader_controller.dart';
|
||||||
|
import 'package:flutter_app/resources/widgets/app_loader_widget.dart';
|
||||||
|
import 'package:flutter_app/resources/widgets/no_results_for_products_widget.dart';
|
||||||
|
import 'package:flutter_app/resources/widgets/product_review_item_container_widget.dart';
|
||||||
|
import 'package:flutter_rating_bar/flutter_rating_bar.dart';
|
||||||
|
import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart';
|
||||||
|
import 'package:nylo_framework/nylo_framework.dart';
|
||||||
|
import 'package:pull_to_refresh/pull_to_refresh.dart';
|
||||||
|
import 'package:woosignal/models/response/product_review.dart';
|
||||||
|
import 'package:woosignal/models/response/products.dart';
|
||||||
|
import '../../app/controllers/product_reviews_controller.dart';
|
||||||
|
|
||||||
|
class ProductReviewsPage extends NyStatefulWidget {
|
||||||
|
final ProductReviewsController controller = ProductReviewsController();
|
||||||
|
|
||||||
|
ProductReviewsPage({Key key}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
_ProductReviewsPageState createState() => _ProductReviewsPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ProductReviewsPageState extends NyState<ProductReviewsPage> {
|
||||||
|
final RefreshController _refreshController =
|
||||||
|
RefreshController(initialRefresh: false);
|
||||||
|
Product _product;
|
||||||
|
bool _shouldStopRequests = false, _isLoading = true;
|
||||||
|
final ProductReviewsLoaderController _productReviewsLoaderController =
|
||||||
|
ProductReviewsLoaderController();
|
||||||
|
|
||||||
|
@override
|
||||||
|
widgetDidLoad() async {
|
||||||
|
_product = widget.data() as Product;
|
||||||
|
await fetchProductReviews();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
List<ProductReview> productReviews =
|
||||||
|
_productReviewsLoaderController.getResults();
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title: Text(trans('Reviews')),
|
||||||
|
centerTitle: true,
|
||||||
|
),
|
||||||
|
body: _isLoading
|
||||||
|
? AppLoaderWidget()
|
||||||
|
: SafeArea(
|
||||||
|
child: Column(
|
||||||
|
// shrinkWrap: true,
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
height: mediaQuery.size.height / 5,
|
||||||
|
padding: EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
||||||
|
margin: EdgeInsets.symmetric(vertical: 16),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
border:
|
||||||
|
Border(bottom: BorderSide(color: Colors.black12))),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
child: Text(
|
||||||
|
_product.name,
|
||||||
|
style: Theme.of(context).textTheme.headline6,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
padding: EdgeInsets.symmetric(vertical: 8),
|
||||||
|
child: Text(
|
||||||
|
_product.ratingCount.toString() + " Reviews",
|
||||||
|
style: Theme.of(context).textTheme.bodyText2,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
margin: EdgeInsets.only(right: 8),
|
||||||
|
child: Text(
|
||||||
|
_product.averageRating + " Stars",
|
||||||
|
style: Theme.of(context).textTheme.bodyText2,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
RatingBarIndicator(
|
||||||
|
rating: double.parse(_product.averageRating),
|
||||||
|
itemBuilder: (context, index) => Icon(
|
||||||
|
Icons.star,
|
||||||
|
color: Colors.amber,
|
||||||
|
),
|
||||||
|
itemCount: 5,
|
||||||
|
itemSize: 20.0,
|
||||||
|
direction: Axis.horizontal,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: SmartRefresher(
|
||||||
|
enablePullDown: true,
|
||||||
|
enablePullUp: true,
|
||||||
|
footer: CustomFooter(
|
||||||
|
builder: (BuildContext context, LoadStatus mode) {
|
||||||
|
Widget body;
|
||||||
|
if (mode == LoadStatus.idle) {
|
||||||
|
body = Text(trans("pull up load"));
|
||||||
|
} else if (mode == LoadStatus.loading) {
|
||||||
|
body = CupertinoActivityIndicator();
|
||||||
|
} else if (mode == LoadStatus.failed) {
|
||||||
|
body = Text(trans("Load Failed! Click retry!"));
|
||||||
|
} else if (mode == LoadStatus.canLoading) {
|
||||||
|
body = Text(trans("release to load more"));
|
||||||
|
} else {
|
||||||
|
return SizedBox.shrink();
|
||||||
|
}
|
||||||
|
return Container(
|
||||||
|
height: 55.0,
|
||||||
|
child: Center(child: body),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
controller: _refreshController,
|
||||||
|
onRefresh: _onRefresh,
|
||||||
|
onLoading: _onLoading,
|
||||||
|
child: (productReviews.length != null &&
|
||||||
|
productReviews.isNotEmpty
|
||||||
|
? StaggeredGridView.countBuilder(
|
||||||
|
crossAxisCount: 2,
|
||||||
|
scrollDirection: Axis.vertical,
|
||||||
|
itemCount: productReviews.length,
|
||||||
|
itemBuilder: (BuildContext context, int index) {
|
||||||
|
ProductReview productReview = productReviews[index];
|
||||||
|
return Container(
|
||||||
|
padding: EdgeInsets.symmetric(
|
||||||
|
horizontal: 16, vertical: 8),
|
||||||
|
margin: EdgeInsets.only(bottom: 8),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
border: Border(
|
||||||
|
bottom:
|
||||||
|
BorderSide(color: Colors.black12))),
|
||||||
|
child: ProductReviewItemContainerWidget(
|
||||||
|
productReview: productReview),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
staggeredTileBuilder: (int index) {
|
||||||
|
return StaggeredTile.fit(2);
|
||||||
|
},
|
||||||
|
mainAxisSpacing: 4.0,
|
||||||
|
crossAxisSpacing: 4.0,
|
||||||
|
)
|
||||||
|
: NoResultsForProductsWidget()),
|
||||||
|
))
|
||||||
|
],
|
||||||
|
)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
_onRefresh() async {
|
||||||
|
_productReviewsLoaderController.clear();
|
||||||
|
_shouldStopRequests = false;
|
||||||
|
|
||||||
|
await fetchProductReviews();
|
||||||
|
_refreshController.refreshCompleted();
|
||||||
|
}
|
||||||
|
|
||||||
|
_onLoading() async {
|
||||||
|
await fetchProductReviews();
|
||||||
|
|
||||||
|
if (mounted) {
|
||||||
|
setState(() {});
|
||||||
|
if (_shouldStopRequests) {
|
||||||
|
_refreshController.loadNoData();
|
||||||
|
} else {
|
||||||
|
_refreshController.loadComplete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future fetchProductReviews() async {
|
||||||
|
await _productReviewsLoaderController.loadProductReviews(
|
||||||
|
product: _product,
|
||||||
|
hasResults: (result) {
|
||||||
|
if (result == false) {
|
||||||
|
setState(() {
|
||||||
|
_shouldStopRequests = true;
|
||||||
|
});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
didFinish: () => setState(() {
|
||||||
|
_isLoading = false;
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -10,7 +10,6 @@
|
|||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_spinkit/flutter_spinkit.dart';
|
import 'package:flutter_spinkit/flutter_spinkit.dart';
|
||||||
import 'package:hexcolor/hexcolor.dart';
|
|
||||||
|
|
||||||
class AppLoaderWidget extends StatelessWidget {
|
class AppLoaderWidget extends StatelessWidget {
|
||||||
const AppLoaderWidget({Key key}) : super(key: key);
|
const AppLoaderWidget({Key key}) : super(key: key);
|
||||||
@ -19,6 +18,6 @@ class AppLoaderWidget extends StatelessWidget {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
bool isDark = (Theme.of(context).brightness == Brightness.dark);
|
bool isDark = (Theme.of(context).brightness == Brightness.dark);
|
||||||
return SpinKitDoubleBounce(
|
return SpinKitDoubleBounce(
|
||||||
color: HexColor(!isDark ? "#424242" : "#c7c7c7"));
|
color: Color(!isDark ? 0xFF424242 : 0xFFC7C7C7));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,7 +11,6 @@
|
|||||||
import 'package:auto_size_text/auto_size_text.dart';
|
import 'package:auto_size_text/auto_size_text.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_app/bootstrap/helpers.dart';
|
import 'package:flutter_app/bootstrap/helpers.dart';
|
||||||
import 'package:hexcolor/hexcolor.dart';
|
|
||||||
|
|
||||||
class PrimaryButton extends StatelessWidget {
|
class PrimaryButton extends StatelessWidget {
|
||||||
const PrimaryButton({
|
const PrimaryButton({
|
||||||
@ -54,7 +53,7 @@ class SecondaryButton extends StatelessWidget {
|
|||||||
textStyle: Theme.of(context).textTheme.bodyText1.copyWith(
|
textStyle: Theme.of(context).textTheme.bodyText1.copyWith(
|
||||||
color: Colors.black87,
|
color: Colors.black87,
|
||||||
),
|
),
|
||||||
bgColor: HexColor("#f6f6f9"),
|
bgColor: Color(0xFFF6F6F9),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -25,7 +25,7 @@ class CheckoutCouponAmountWidget extends StatelessWidget {
|
|||||||
return Text("");
|
return Text("");
|
||||||
} else {
|
} else {
|
||||||
if (checkoutSession.coupon == null) {
|
if (checkoutSession.coupon == null) {
|
||||||
return Container();
|
return SizedBox.shrink();
|
||||||
}
|
}
|
||||||
return Padding(
|
return Padding(
|
||||||
child: CheckoutMetaLine(
|
child: CheckoutMetaLine(
|
||||||
|
|||||||
@ -23,7 +23,7 @@ class CheckoutShippingTypeWidget extends StatelessWidget {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
bool hasDisableShipping = wooSignalApp.disableShipping == 1;
|
bool hasDisableShipping = wooSignalApp.disableShipping == 1;
|
||||||
if (hasDisableShipping == true) {
|
if (hasDisableShipping == true) {
|
||||||
return Container();
|
return SizedBox.shrink();
|
||||||
}
|
}
|
||||||
bool hasSelectedShippingType = checkoutSession.shippingType != null;
|
bool hasSelectedShippingType = checkoutSession.shippingType != null;
|
||||||
return CheckoutRowLine(
|
return CheckoutRowLine(
|
||||||
@ -42,10 +42,12 @@ class CheckoutShippingTypeWidget extends StatelessWidget {
|
|||||||
CustomerAddress shippingAddress =
|
CustomerAddress shippingAddress =
|
||||||
checkoutSession.billingDetails.shippingAddress;
|
checkoutSession.billingDetails.shippingAddress;
|
||||||
if (shippingAddress == null || shippingAddress.customerCountry == null) {
|
if (shippingAddress == null || shippingAddress.customerCountry == null) {
|
||||||
showToastNotification(context,
|
showToastNotification(
|
||||||
title: trans("Oops"),
|
context,
|
||||||
description: trans("Add your shipping details first"),
|
title: trans("Oops"),
|
||||||
icon: Icons.local_shipping);
|
description: trans("Add your shipping details first"),
|
||||||
|
icon: Icons.local_shipping,
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Navigator.pushNamed(context, "/checkout-shipping-type")
|
Navigator.pushNamed(context, "/checkout-shipping-type")
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
import 'package:auto_size_text/auto_size_text.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_app/bootstrap/helpers.dart';
|
import 'package:flutter_app/bootstrap/helpers.dart';
|
||||||
import 'package:flutter_app/resources/widgets/app_loader_widget.dart';
|
import 'package:flutter_app/resources/widgets/app_loader_widget.dart';
|
||||||
@ -29,16 +30,18 @@ class _CompoHomeWidgetState extends State<CompoHomeWidget> {
|
|||||||
|
|
||||||
_loadHome() async {
|
_loadHome() async {
|
||||||
categories = await appWooSignal((api) =>
|
categories = await appWooSignal((api) =>
|
||||||
api.getProductCategories(parent: 0, perPage: 20, hideEmpty: true));
|
api.getProductCategories(parent: 0, perPage: 50, hideEmpty: true));
|
||||||
categories.sort((category1, category2) =>
|
categories.sort((category1, category2) =>
|
||||||
category1.menuOrder.compareTo(category2.menuOrder));
|
category1.menuOrder.compareTo(category2.menuOrder));
|
||||||
|
|
||||||
for (var category in categories) {
|
for (var category in categories) {
|
||||||
List<Product> products = await appWooSignal((api) => api.getProducts(
|
List<Product> products = await appWooSignal(
|
||||||
perPage: 10,
|
(api) => api.getProducts(
|
||||||
category: category.id.toString(),
|
perPage: 10,
|
||||||
status: "publish",
|
category: category.id.toString(),
|
||||||
stockStatus: "instock"));
|
status: "publish",
|
||||||
|
stockStatus: "instock"),
|
||||||
|
);
|
||||||
if (products.isNotEmpty) {
|
if (products.isNotEmpty) {
|
||||||
categoryAndProducts.addAll({category: products});
|
categoryAndProducts.addAll({category: products});
|
||||||
setState(() {});
|
setState(() {});
|
||||||
@ -106,7 +109,8 @@ class _CompoHomeWidgetState extends State<CompoHomeWidget> {
|
|||||||
width: MediaQuery.of(context).size.width,
|
width: MediaQuery.of(context).size.width,
|
||||||
fit: BoxFit.cover,
|
fit: BoxFit.cover,
|
||||||
),
|
),
|
||||||
onTap: () => _showCategory(catProds.key))
|
onTap: () => _showCategory(catProds.key),
|
||||||
|
)
|
||||||
: null,
|
: null,
|
||||||
ConstrainedBox(
|
ConstrainedBox(
|
||||||
constraints: BoxConstraints(
|
constraints: BoxConstraints(
|
||||||
@ -123,7 +127,7 @@ class _CompoHomeWidgetState extends State<CompoHomeWidget> {
|
|||||||
MainAxisAlignment.spaceBetween,
|
MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Text(
|
child: AutoSizeText(
|
||||||
catProds.key.name,
|
catProds.key.name,
|
||||||
style: Theme.of(context)
|
style: Theme.of(context)
|
||||||
.textTheme
|
.textTheme
|
||||||
@ -131,6 +135,7 @@ class _CompoHomeWidgetState extends State<CompoHomeWidget> {
|
|||||||
.copyWith(
|
.copyWith(
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
fontSize: 22),
|
fontSize: 22),
|
||||||
|
maxLines: 1,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Flexible(
|
Flexible(
|
||||||
|
|||||||
43
LabelStoreMax/lib/resources/widgets/future_build_widget.dart
Normal file
43
LabelStoreMax/lib/resources/widgets/future_build_widget.dart
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
// Label StoreMax
|
||||||
|
//
|
||||||
|
// Created by Anthony Gordon.
|
||||||
|
// 2022, WooSignal Ltd. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class FutureBuildWidget<T> extends StatelessWidget {
|
||||||
|
const FutureBuildWidget(
|
||||||
|
{Key key,
|
||||||
|
@required this.asyncFuture,
|
||||||
|
@required this.onValue,
|
||||||
|
this.onLoading})
|
||||||
|
: super(key: key);
|
||||||
|
|
||||||
|
final Widget Function(T value) onValue;
|
||||||
|
final Widget onLoading;
|
||||||
|
final Future asyncFuture;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return FutureBuilder<T>(
|
||||||
|
future: asyncFuture,
|
||||||
|
builder: (BuildContext context, AsyncSnapshot<T> snapshot) {
|
||||||
|
switch (snapshot.connectionState) {
|
||||||
|
case ConnectionState.waiting:
|
||||||
|
return onLoading ?? Container();
|
||||||
|
default:
|
||||||
|
if (snapshot.hasError) {
|
||||||
|
return SizedBox.shrink();
|
||||||
|
} else {
|
||||||
|
return onValue(snapshot.data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -57,6 +57,8 @@ class _HomeDrawerWidgetState extends State<HomeDrawerWidget> {
|
|||||||
),
|
),
|
||||||
if (["compo"].contains(_themeType) == false)
|
if (["compo"].contains(_themeType) == false)
|
||||||
Column(
|
Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Padding(
|
Padding(
|
||||||
child: Text(
|
child: Text(
|
||||||
|
|||||||
@ -161,7 +161,7 @@ class _NoticHomeWidgetState extends State<NoticHomeWidget> {
|
|||||||
} else if (mode == LoadStatus.canLoading) {
|
} else if (mode == LoadStatus.canLoading) {
|
||||||
body = Text(trans("release to load more"));
|
body = Text(trans("release to load more"));
|
||||||
} else {
|
} else {
|
||||||
return Container();
|
return SizedBox.shrink();
|
||||||
}
|
}
|
||||||
return Container(
|
return Container(
|
||||||
height: 55.0,
|
height: 55.0,
|
||||||
|
|||||||
@ -60,21 +60,22 @@ class _NoticThemeWidgetState extends State<NoticThemeWidget> {
|
|||||||
body: activeWidget,
|
body: activeWidget,
|
||||||
resizeToAvoidBottomInset: false,
|
resizeToAvoidBottomInset: false,
|
||||||
bottomNavigationBar: allNavWidgets == null
|
bottomNavigationBar: allNavWidgets == null
|
||||||
? AppLoaderWidget() : BottomNavigationBar(
|
? AppLoaderWidget()
|
||||||
onTap: (currentIndex) =>
|
: BottomNavigationBar(
|
||||||
_changeMainWidget(currentIndex, allNavWidgets),
|
onTap: (currentIndex) =>
|
||||||
currentIndex: _currentIndex,
|
_changeMainWidget(currentIndex, allNavWidgets),
|
||||||
unselectedItemColor: Colors.black54,
|
currentIndex: _currentIndex,
|
||||||
fixedColor: Colors.black87,
|
unselectedItemColor: Colors.black54,
|
||||||
selectedLabelStyle: TextStyle(color: Colors.black),
|
fixedColor: Colors.black87,
|
||||||
unselectedLabelStyle: TextStyle(
|
selectedLabelStyle: TextStyle(color: Colors.black),
|
||||||
color: Colors.black87,
|
unselectedLabelStyle: TextStyle(
|
||||||
),
|
color: Colors.black87,
|
||||||
showSelectedLabels: false,
|
),
|
||||||
showUnselectedLabels: false,
|
showSelectedLabels: false,
|
||||||
items:
|
showUnselectedLabels: false,
|
||||||
allNavWidgets.map((e) => e.bottomNavigationBarItem).toList(),
|
items:
|
||||||
),
|
allNavWidgets.map((e) => e.bottomNavigationBarItem).toList(),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -122,12 +123,12 @@ class _NoticThemeWidgetState extends State<NoticThemeWidget> {
|
|||||||
items.add(BottomNavItem(
|
items.add(BottomNavItem(
|
||||||
id: 5,
|
id: 5,
|
||||||
bottomNavigationBarItem:
|
bottomNavigationBarItem:
|
||||||
BottomNavigationBarItem(icon: Icon(Icons.person), label: 'Account'),
|
BottomNavigationBarItem(icon: Icon(Icons.person), label: 'Account'),
|
||||||
tabWidget: (await authCheck())
|
tabWidget: (await authCheck())
|
||||||
? AccountDetailPage(showLeadingBackButton: false)
|
? AccountDetailPage(showLeadingBackButton: false)
|
||||||
: AccountLandingPage(
|
: AccountLandingPage(
|
||||||
showBackButton: false,
|
showBackButton: false,
|
||||||
),
|
),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
return items;
|
return items;
|
||||||
|
|||||||
@ -0,0 +1,65 @@
|
|||||||
|
// Label StoreMax
|
||||||
|
//
|
||||||
|
// Created by Anthony Gordon.
|
||||||
|
// 2022, WooSignal Ltd. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_app/resources/widgets/product_detail_description_widget.dart';
|
||||||
|
import 'package:flutter_app/resources/widgets/product_detail_header_widget.dart';
|
||||||
|
import 'package:flutter_app/resources/widgets/product_detail_image_swiper_widget.dart';
|
||||||
|
import 'package:flutter_app/resources/widgets/product_detail_related_products_widget.dart';
|
||||||
|
import 'package:flutter_app/resources/widgets/product_detail_reviews_widget.dart';
|
||||||
|
import 'package:flutter_app/resources/widgets/product_detail_upsell_widget.dart';
|
||||||
|
import 'package:woosignal/models/response/products.dart';
|
||||||
|
import 'package:woosignal/models/response/woosignal_app.dart';
|
||||||
|
|
||||||
|
class ProductDetailBodyWidget extends StatelessWidget {
|
||||||
|
const ProductDetailBodyWidget(
|
||||||
|
{Key key, @required this.product, @required this.wooSignalApp})
|
||||||
|
: super(key: key);
|
||||||
|
|
||||||
|
final Product product;
|
||||||
|
final WooSignalApp wooSignalApp;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return ListView(
|
||||||
|
shrinkWrap: true,
|
||||||
|
children: <Widget>[
|
||||||
|
ProductDetailImageSwiperWidget(
|
||||||
|
product: product,
|
||||||
|
onTapImage: (i) => _viewProductImages(context, i)),
|
||||||
|
// </Image Swiper>
|
||||||
|
|
||||||
|
ProductDetailHeaderWidget(product: product),
|
||||||
|
// </Header title + price>
|
||||||
|
|
||||||
|
ProductDetailDescriptionWidget(product: product),
|
||||||
|
// </Description body>
|
||||||
|
|
||||||
|
ProductDetailReviewsWidget(
|
||||||
|
product: product, wooSignalApp: wooSignalApp),
|
||||||
|
// </Product reviews>
|
||||||
|
|
||||||
|
ProductDetailUpsellWidget(
|
||||||
|
productIds: product.upsellIds, wooSignalApp: wooSignalApp),
|
||||||
|
// </You may also like>
|
||||||
|
|
||||||
|
ProductDetailRelatedProductsWidget(
|
||||||
|
product: product, wooSignalApp: wooSignalApp)
|
||||||
|
// </Related products>
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
_viewProductImages(BuildContext context, int i) =>
|
||||||
|
Navigator.pushNamed(context, "/product-images", arguments: {
|
||||||
|
"index": i,
|
||||||
|
"images": product.images.map((f) => f.src).toList()
|
||||||
|
});
|
||||||
|
}
|
||||||
@ -0,0 +1,90 @@
|
|||||||
|
// Label StoreMax
|
||||||
|
//
|
||||||
|
// Created by Anthony Gordon.
|
||||||
|
// 2022, WooSignal Ltd. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_app/resources/widgets/woosignal_ui.dart';
|
||||||
|
import 'package:flutter_widget_from_html/flutter_widget_from_html.dart';
|
||||||
|
import 'package:nylo_framework/nylo_framework.dart';
|
||||||
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
|
import 'package:woosignal/models/response/products.dart';
|
||||||
|
|
||||||
|
class ProductDetailDescriptionWidget extends StatelessWidget {
|
||||||
|
const ProductDetailDescriptionWidget({Key key, @required this.product})
|
||||||
|
: super(key: key);
|
||||||
|
|
||||||
|
final Product product;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
if (product.shortDescription.isEmpty && product.description.isEmpty) {
|
||||||
|
return SizedBox.shrink();
|
||||||
|
}
|
||||||
|
|
||||||
|
return ListView(
|
||||||
|
shrinkWrap: true,
|
||||||
|
physics: NeverScrollableScrollPhysics(),
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
height: 50,
|
||||||
|
padding: EdgeInsets.symmetric(vertical: 4, horizontal: 16),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: <Widget>[
|
||||||
|
Text(
|
||||||
|
trans("Description"),
|
||||||
|
style:
|
||||||
|
Theme.of(context).textTheme.caption.copyWith(fontSize: 18),
|
||||||
|
textAlign: TextAlign.left,
|
||||||
|
),
|
||||||
|
if (product.shortDescription.isNotEmpty &&
|
||||||
|
product.description.isNotEmpty)
|
||||||
|
MaterialButton(
|
||||||
|
child: Text(
|
||||||
|
trans("Full description"),
|
||||||
|
style: Theme.of(context)
|
||||||
|
.textTheme
|
||||||
|
.bodyText2
|
||||||
|
.copyWith(fontSize: 14),
|
||||||
|
textAlign: TextAlign.right,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
height: 50,
|
||||||
|
minWidth: 60,
|
||||||
|
onPressed: () => _modalBottomSheetMenu(context),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
padding: EdgeInsets.symmetric(vertical: 4, horizontal: 16),
|
||||||
|
child: HtmlWidget(
|
||||||
|
product.shortDescription.isNotEmpty
|
||||||
|
? product.shortDescription
|
||||||
|
: product.description,
|
||||||
|
renderMode: RenderMode.column, onTapUrl: (String url) async {
|
||||||
|
await launch(url);
|
||||||
|
return true;
|
||||||
|
}, textStyle: Theme.of(context).textTheme.bodyText2),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
_modalBottomSheetMenu(BuildContext context) {
|
||||||
|
wsModalBottom(
|
||||||
|
context,
|
||||||
|
title: trans("Description"),
|
||||||
|
bodyWidget: SingleChildScrollView(
|
||||||
|
child: HtmlWidget(product.description),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,128 @@
|
|||||||
|
// Label StoreMax
|
||||||
|
//
|
||||||
|
// Created by Anthony Gordon.
|
||||||
|
// 2022, WooSignal Ltd. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_app/bootstrap/helpers.dart';
|
||||||
|
import 'package:flutter_app/resources/widgets/buttons.dart';
|
||||||
|
import 'package:nylo_framework/nylo_framework.dart';
|
||||||
|
import 'package:woosignal/models/response/products.dart';
|
||||||
|
|
||||||
|
class ProductDetailFooterActionsWidget extends StatelessWidget {
|
||||||
|
const ProductDetailFooterActionsWidget(
|
||||||
|
{Key key,
|
||||||
|
@required this.product,
|
||||||
|
@required this.quantity,
|
||||||
|
@required this.onAddToCart,
|
||||||
|
@required this.onViewExternalProduct,
|
||||||
|
@required this.onAddQuantity,
|
||||||
|
@required this.onRemoveQuantity})
|
||||||
|
: super(key: key);
|
||||||
|
final Product product;
|
||||||
|
final Function onViewExternalProduct;
|
||||||
|
final Function onAddToCart;
|
||||||
|
final Function onAddQuantity;
|
||||||
|
final Function onRemoveQuantity;
|
||||||
|
final int quantity;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Container(
|
||||||
|
height: 140,
|
||||||
|
padding: EdgeInsets.symmetric(horizontal: 16),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: ThemeColor.get(context).background,
|
||||||
|
boxShadow: [
|
||||||
|
BoxShadow(
|
||||||
|
color: Colors.black12,
|
||||||
|
blurRadius: 15.0,
|
||||||
|
spreadRadius: -17,
|
||||||
|
offset: Offset(
|
||||||
|
0,
|
||||||
|
-10,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||||
|
children: <Widget>[
|
||||||
|
(product.type != "external"
|
||||||
|
? Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: <Widget>[
|
||||||
|
Text(
|
||||||
|
trans("Quantity"),
|
||||||
|
style: Theme.of(context)
|
||||||
|
.textTheme
|
||||||
|
.bodyText1
|
||||||
|
.copyWith(color: Colors.grey),
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
children: <Widget>[
|
||||||
|
IconButton(
|
||||||
|
icon: Icon(
|
||||||
|
Icons.remove_circle_outline,
|
||||||
|
size: 28,
|
||||||
|
),
|
||||||
|
onPressed: onRemoveQuantity,
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
quantity.toString(),
|
||||||
|
style: Theme.of(context).textTheme.bodyText1,
|
||||||
|
),
|
||||||
|
IconButton(
|
||||||
|
icon: Icon(
|
||||||
|
Icons.add_circle_outline,
|
||||||
|
size: 28,
|
||||||
|
),
|
||||||
|
onPressed: onAddQuantity,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
],
|
||||||
|
)
|
||||||
|
: null),
|
||||||
|
Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: <Widget>[
|
||||||
|
Flexible(
|
||||||
|
child: Align(
|
||||||
|
child: Text(
|
||||||
|
formatStringCurrency(
|
||||||
|
total:
|
||||||
|
(parseWcPrice(product.price) * quantity).toString()),
|
||||||
|
style: Theme.of(context).textTheme.headline4,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
alignment: Alignment.centerLeft,
|
||||||
|
)),
|
||||||
|
product.type == "external"
|
||||||
|
? Flexible(
|
||||||
|
child: PrimaryButton(
|
||||||
|
title: trans("Buy Product"),
|
||||||
|
action: () => onViewExternalProduct,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: Flexible(
|
||||||
|
child: PrimaryButton(
|
||||||
|
title: trans("Add to cart"),
|
||||||
|
action: onAddToCart,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
].where((e) => e != null).toList(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,73 @@
|
|||||||
|
// Label StoreMax
|
||||||
|
//
|
||||||
|
// Created by Anthony Gordon.
|
||||||
|
// 2022, WooSignal Ltd. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_app/bootstrap/helpers.dart';
|
||||||
|
import 'package:woosignal/models/response/products.dart';
|
||||||
|
|
||||||
|
class ProductDetailHeaderWidget extends StatelessWidget {
|
||||||
|
const ProductDetailHeaderWidget({Key key, @required this.product})
|
||||||
|
: super(key: key);
|
||||||
|
|
||||||
|
final Product product;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Container(
|
||||||
|
height: 100,
|
||||||
|
padding: EdgeInsets.symmetric(
|
||||||
|
vertical: 10,
|
||||||
|
horizontal: 16,
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: <Widget>[
|
||||||
|
Flexible(
|
||||||
|
child: Text(
|
||||||
|
product.name,
|
||||||
|
style:
|
||||||
|
Theme.of(context).textTheme.bodyText1.copyWith(fontSize: 20),
|
||||||
|
textAlign: TextAlign.left,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
maxLines: 2,
|
||||||
|
),
|
||||||
|
flex: 4,
|
||||||
|
),
|
||||||
|
Flexible(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.end,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: <Widget>[
|
||||||
|
Text(
|
||||||
|
formatStringCurrency(total: product.price),
|
||||||
|
style: Theme.of(context).textTheme.headline4.copyWith(
|
||||||
|
fontSize: 20,
|
||||||
|
),
|
||||||
|
textAlign: TextAlign.right,
|
||||||
|
),
|
||||||
|
(product.onSale == true && product.type != "variable"
|
||||||
|
? Text(
|
||||||
|
formatStringCurrency(total: product.regularPrice),
|
||||||
|
style: TextStyle(
|
||||||
|
color: Colors.grey,
|
||||||
|
decoration: TextDecoration.lineThrough,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: null)
|
||||||
|
].where((t) => t != null).toList(),
|
||||||
|
),
|
||||||
|
flex: 2,
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,44 @@
|
|||||||
|
// Label StoreMax
|
||||||
|
//
|
||||||
|
// Created by Anthony Gordon.
|
||||||
|
// 2022, WooSignal Ltd. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_app/resources/widgets/cached_image_widget.dart';
|
||||||
|
import 'package:flutter_swiper/flutter_swiper.dart';
|
||||||
|
import 'package:nylo_framework/nylo_framework.dart';
|
||||||
|
import 'package:woosignal/models/response/products.dart';
|
||||||
|
|
||||||
|
class ProductDetailImageSwiperWidget extends StatelessWidget {
|
||||||
|
const ProductDetailImageSwiperWidget(
|
||||||
|
{Key key, @required this.product, @required this.onTapImage})
|
||||||
|
: super(key: key);
|
||||||
|
|
||||||
|
final Product product;
|
||||||
|
final void Function(int i) onTapImage;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return SizedBox(
|
||||||
|
height: MediaQuery.of(context).size.height * 0.40,
|
||||||
|
child: SizedBox(
|
||||||
|
child: Swiper(
|
||||||
|
itemBuilder: (BuildContext context, int index) => CachedImageWidget(
|
||||||
|
image: product.images.isNotEmpty
|
||||||
|
? product.images[index].src
|
||||||
|
: getEnv("PRODUCT_PLACEHOLDER_IMAGE"),
|
||||||
|
),
|
||||||
|
itemCount: product.images.isEmpty ? 1 : product.images.length,
|
||||||
|
viewportFraction: 0.85,
|
||||||
|
scale: 0.9,
|
||||||
|
onTap: onTapImage,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,80 @@
|
|||||||
|
// Label StoreMax
|
||||||
|
//
|
||||||
|
// Created by Anthony Gordon.
|
||||||
|
// 2022, WooSignal Ltd. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_app/bootstrap/helpers.dart';
|
||||||
|
import 'package:flutter_app/resources/widgets/app_loader_widget.dart';
|
||||||
|
import 'package:flutter_app/resources/widgets/future_build_widget.dart';
|
||||||
|
import 'package:flutter_app/resources/widgets/woosignal_ui.dart';
|
||||||
|
import 'package:nylo_framework/nylo_framework.dart';
|
||||||
|
import 'package:woosignal/models/response/products.dart';
|
||||||
|
import 'package:woosignal/models/response/woosignal_app.dart';
|
||||||
|
|
||||||
|
class ProductDetailRelatedProductsWidget extends StatelessWidget {
|
||||||
|
const ProductDetailRelatedProductsWidget(
|
||||||
|
{Key key, @required this.product, @required this.wooSignalApp})
|
||||||
|
: super(key: key);
|
||||||
|
|
||||||
|
final Product product;
|
||||||
|
final WooSignalApp wooSignalApp;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
if (wooSignalApp.showRelatedProducts == false) {
|
||||||
|
return SizedBox.shrink();
|
||||||
|
}
|
||||||
|
return ListView(
|
||||||
|
shrinkWrap: true,
|
||||||
|
physics: NeverScrollableScrollPhysics(),
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
height: 50,
|
||||||
|
padding: EdgeInsets.symmetric(vertical: 4, horizontal: 16),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: <Widget>[
|
||||||
|
Text(
|
||||||
|
trans("Related products"),
|
||||||
|
style:
|
||||||
|
Theme.of(context).textTheme.caption.copyWith(fontSize: 18),
|
||||||
|
textAlign: TextAlign.left,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
height: 200,
|
||||||
|
child: FutureBuildWidget<List<Product>>(
|
||||||
|
asyncFuture: fetchRelated(),
|
||||||
|
onValue: (relatedProducts) {
|
||||||
|
return ListView(
|
||||||
|
shrinkWrap: true,
|
||||||
|
scrollDirection: Axis.horizontal,
|
||||||
|
children: relatedProducts
|
||||||
|
.map((e) => Container(
|
||||||
|
width: MediaQuery.of(context).size.width / 2.2,
|
||||||
|
child: ProductItemContainer(product: e)))
|
||||||
|
.toList(),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
onLoading: AppLoaderWidget(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<List<Product>> fetchRelated() async {
|
||||||
|
return await appWooSignal(
|
||||||
|
(api) => api.getProducts(perPage: 100, include: product.relatedIds),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,90 @@
|
|||||||
|
// Label StoreMax
|
||||||
|
//
|
||||||
|
// Created by Anthony Gordon.
|
||||||
|
// 2022, WooSignal Ltd. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_app/bootstrap/helpers.dart';
|
||||||
|
import 'package:flutter_rating_bar/flutter_rating_bar.dart';
|
||||||
|
import 'package:nylo_framework/nylo_framework.dart';
|
||||||
|
import 'package:woosignal/models/response/product_review.dart';
|
||||||
|
|
||||||
|
class ProductDetailReviewTileWidget extends StatefulWidget {
|
||||||
|
ProductDetailReviewTileWidget({Key key, @required this.productReview});
|
||||||
|
final ProductReview productReview;
|
||||||
|
|
||||||
|
@override
|
||||||
|
_ProductDetailReviewTileWidgetState createState() =>
|
||||||
|
_ProductDetailReviewTileWidgetState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ProductDetailReviewTileWidgetState
|
||||||
|
extends State<ProductDetailReviewTileWidget> {
|
||||||
|
int _maxLines = 3;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Container(
|
||||||
|
padding: EdgeInsets.only(top: 5, left: 16, right: 16),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
RatingBarIndicator(
|
||||||
|
rating: widget.productReview.rating.toDouble(),
|
||||||
|
itemBuilder: (context, index) => Icon(
|
||||||
|
Icons.star,
|
||||||
|
color: Colors.amber,
|
||||||
|
),
|
||||||
|
itemCount: 5,
|
||||||
|
itemSize: 20.0,
|
||||||
|
direction: Axis.horizontal,
|
||||||
|
),
|
||||||
|
Text(widget.productReview.reviewer),
|
||||||
|
Text(
|
||||||
|
formatDateTime("MMM d, yyyy").format(
|
||||||
|
parseDateTime(widget.productReview.dateCreated),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
ListTile(
|
||||||
|
title: Container(
|
||||||
|
margin: EdgeInsets.only(top: 10),
|
||||||
|
child: Text(parseHtmlString(widget.productReview.review),
|
||||||
|
maxLines: _maxLines,
|
||||||
|
overflow: _maxLines != null
|
||||||
|
? TextOverflow.ellipsis
|
||||||
|
: TextOverflow.visible),
|
||||||
|
),
|
||||||
|
contentPadding: EdgeInsets.all(0),
|
||||||
|
minVerticalPadding: 0),
|
||||||
|
if (_maxLines != null && widget.productReview.review.length > 115)
|
||||||
|
InkWell(
|
||||||
|
child: Text(trans("More"),
|
||||||
|
style: Theme.of(context)
|
||||||
|
.textTheme
|
||||||
|
.bodyText2
|
||||||
|
.copyWith(fontWeight: FontWeight.bold)),
|
||||||
|
onTap: () => setState(() {
|
||||||
|
_maxLines = null;
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
border: Border(
|
||||||
|
bottom: BorderSide(color: Colors.black12, width: 1),
|
||||||
|
)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,152 @@
|
|||||||
|
// Label StoreMax
|
||||||
|
//
|
||||||
|
// Created by Anthony Gordon.
|
||||||
|
// 2022, WooSignal Ltd. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
|
||||||
|
import 'package:flutter/cupertino.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_app/bootstrap/helpers.dart';
|
||||||
|
import 'package:flutter_app/resources/widgets/future_build_widget.dart';
|
||||||
|
import 'package:flutter_app/resources/widgets/product_detail_review_tile_widget.dart';
|
||||||
|
import 'package:flutter_rating_bar/flutter_rating_bar.dart';
|
||||||
|
import 'package:nylo_framework/nylo_framework.dart';
|
||||||
|
import 'package:woosignal/models/response/product_review.dart';
|
||||||
|
import 'package:woosignal/models/response/products.dart';
|
||||||
|
import 'package:woosignal/models/response/woosignal_app.dart';
|
||||||
|
|
||||||
|
class ProductDetailReviewsWidget extends StatefulWidget {
|
||||||
|
ProductDetailReviewsWidget(
|
||||||
|
{@required this.product, @required this.wooSignalApp});
|
||||||
|
final Product product;
|
||||||
|
final WooSignalApp wooSignalApp;
|
||||||
|
|
||||||
|
@override
|
||||||
|
_ProductDetailReviewsWidgetState createState() =>
|
||||||
|
_ProductDetailReviewsWidgetState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ProductDetailReviewsWidgetState
|
||||||
|
extends State<ProductDetailReviewsWidget> {
|
||||||
|
bool _ratingExpanded = false;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
if (widget.product.reviewsAllowed == false ||
|
||||||
|
widget.wooSignalApp.showProductReviews == false) {
|
||||||
|
return SizedBox.shrink();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Row(
|
||||||
|
children: <Widget>[
|
||||||
|
Expanded(
|
||||||
|
child: ExpansionTile(
|
||||||
|
textColor: ThemeColor.get(context).primaryAccent,
|
||||||
|
iconColor: ThemeColor.get(context).primaryAccent,
|
||||||
|
tilePadding: EdgeInsets.symmetric(horizontal: 16),
|
||||||
|
childrenPadding: EdgeInsets.all(0),
|
||||||
|
title: Text("${trans("Reviews")} (${widget.product.ratingCount})"),
|
||||||
|
onExpansionChanged: (value) {
|
||||||
|
setState(() {
|
||||||
|
_ratingExpanded = value;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
trailing: Container(
|
||||||
|
width: MediaQuery.of(context).size.width / 1.8,
|
||||||
|
child: Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
|
children: [
|
||||||
|
RatingBarIndicator(
|
||||||
|
rating: double.parse(widget.product.averageRating),
|
||||||
|
itemBuilder: (context, index) => Icon(
|
||||||
|
Icons.star,
|
||||||
|
color: Colors.amber,
|
||||||
|
),
|
||||||
|
itemCount: 5,
|
||||||
|
itemSize: 25.0,
|
||||||
|
direction: Axis.horizontal,
|
||||||
|
),
|
||||||
|
Icon(
|
||||||
|
_ratingExpanded
|
||||||
|
? Icons.keyboard_arrow_down_rounded
|
||||||
|
: Icons.keyboard_arrow_up_rounded,
|
||||||
|
size: 30)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
initiallyExpanded: false,
|
||||||
|
children: [
|
||||||
|
_ratingExpanded == true
|
||||||
|
? FutureBuildWidget<List<ProductReview>>(
|
||||||
|
asyncFuture: fetchReviews(),
|
||||||
|
onValue: (reviews) {
|
||||||
|
int reviewsCount = reviews.length;
|
||||||
|
List<Widget> childrenWidgets = [];
|
||||||
|
List<ProductDetailReviewTileWidget> children = reviews
|
||||||
|
.map((review) => ProductDetailReviewTileWidget(
|
||||||
|
productReview: review))
|
||||||
|
.toList();
|
||||||
|
childrenWidgets.addAll(children);
|
||||||
|
|
||||||
|
if (reviewsCount >= 5) {
|
||||||
|
childrenWidgets.add(
|
||||||
|
Container(
|
||||||
|
child: ListTile(
|
||||||
|
contentPadding:
|
||||||
|
EdgeInsets.symmetric(horizontal: 16),
|
||||||
|
title: Text(
|
||||||
|
trans('See More Reviews'),
|
||||||
|
),
|
||||||
|
onTap: () => Navigator.pushNamed(
|
||||||
|
context, "/product-reviews",
|
||||||
|
arguments: widget.product),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return ListView(
|
||||||
|
shrinkWrap: true,
|
||||||
|
physics: NeverScrollableScrollPhysics(),
|
||||||
|
padding: EdgeInsets.all(0),
|
||||||
|
children: reviews.isEmpty
|
||||||
|
? [
|
||||||
|
Container(
|
||||||
|
child: ListTile(
|
||||||
|
title: Text(
|
||||||
|
trans('There are no reviews yet.'),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
]
|
||||||
|
: childrenWidgets,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
onLoading: Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 8),
|
||||||
|
child: CupertinoActivityIndicator(),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: null,
|
||||||
|
].where((element) => element != null).toList(),
|
||||||
|
)),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<List<ProductReview>> fetchReviews() async {
|
||||||
|
return await appWooSignal(
|
||||||
|
(api) => api.getProductReviews(
|
||||||
|
perPage: 5, product: [widget.product.id], status: "approved"),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,170 @@
|
|||||||
|
// Label StoreMax
|
||||||
|
//
|
||||||
|
// Created by Anthony Gordon.
|
||||||
|
// 2022, WooSignal Ltd. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
|
||||||
|
import 'package:flutter/cupertino.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_app/app/controllers/product_loader_controller.dart';
|
||||||
|
import 'package:flutter_app/resources/widgets/app_loader_widget.dart';
|
||||||
|
import 'package:flutter_app/resources/widgets/no_results_for_products_widget.dart';
|
||||||
|
import 'package:flutter_app/resources/widgets/woosignal_ui.dart';
|
||||||
|
import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart';
|
||||||
|
import 'package:nylo_framework/nylo_framework.dart';
|
||||||
|
import 'package:pull_to_refresh/pull_to_refresh.dart';
|
||||||
|
import 'package:woosignal/models/response/products.dart';
|
||||||
|
import 'package:woosignal/models/response/woosignal_app.dart';
|
||||||
|
|
||||||
|
class ProductDetailUpsellWidget extends StatefulWidget {
|
||||||
|
ProductDetailUpsellWidget(
|
||||||
|
{@required this.productIds, @required this.wooSignalApp});
|
||||||
|
final List<int> productIds;
|
||||||
|
final WooSignalApp wooSignalApp;
|
||||||
|
|
||||||
|
@override
|
||||||
|
_ProductDetailUpsellWidgetState createState() =>
|
||||||
|
_ProductDetailUpsellWidgetState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ProductDetailUpsellWidgetState extends State<ProductDetailUpsellWidget> {
|
||||||
|
final RefreshController _refreshControllerUpsell =
|
||||||
|
RefreshController(initialRefresh: false);
|
||||||
|
|
||||||
|
final ProductLoaderController _productLoaderController =
|
||||||
|
ProductLoaderController();
|
||||||
|
|
||||||
|
bool _shouldStopRequests = false, _isLoading = true;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
fetchProducts();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
List<Product> products = _productLoaderController.getResults();
|
||||||
|
if (widget.productIds.isEmpty ||
|
||||||
|
products.isEmpty ||
|
||||||
|
widget.wooSignalApp.showUpsellProducts == false) {
|
||||||
|
return SizedBox.shrink();
|
||||||
|
}
|
||||||
|
return ListView(
|
||||||
|
shrinkWrap: true,
|
||||||
|
physics: NeverScrollableScrollPhysics(),
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
height: 50,
|
||||||
|
padding: EdgeInsets.symmetric(vertical: 4, horizontal: 16),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: <Widget>[
|
||||||
|
Text(
|
||||||
|
trans("${trans('You may also like')}…"),
|
||||||
|
style:
|
||||||
|
Theme.of(context).textTheme.caption.copyWith(fontSize: 18),
|
||||||
|
textAlign: TextAlign.left,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
_isLoading == true
|
||||||
|
? AppLoaderWidget()
|
||||||
|
: Container(
|
||||||
|
height: 200,
|
||||||
|
child: SmartRefresher(
|
||||||
|
enablePullDown: true,
|
||||||
|
enablePullUp: true,
|
||||||
|
footer: CustomFooter(
|
||||||
|
builder: (BuildContext context, LoadStatus mode) {
|
||||||
|
Widget body;
|
||||||
|
if (mode == LoadStatus.idle) {
|
||||||
|
body = Text(trans("pull up load"));
|
||||||
|
} else if (mode == LoadStatus.loading) {
|
||||||
|
body = CupertinoActivityIndicator();
|
||||||
|
} else if (mode == LoadStatus.failed) {
|
||||||
|
body = Text(trans("Load Failed! Click retry!"));
|
||||||
|
} else if (mode == LoadStatus.canLoading) {
|
||||||
|
body = Text(trans("release to load more"));
|
||||||
|
} else {
|
||||||
|
return SizedBox.shrink();
|
||||||
|
}
|
||||||
|
return Container(
|
||||||
|
height: 55.0,
|
||||||
|
child: Center(child: body),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
controller: _refreshControllerUpsell,
|
||||||
|
onRefresh: _onRefresh,
|
||||||
|
onLoading: _onLoading,
|
||||||
|
child: (products.length != null && products.isNotEmpty
|
||||||
|
? StaggeredGridView.countBuilder(
|
||||||
|
crossAxisCount: 2,
|
||||||
|
scrollDirection: Axis.horizontal,
|
||||||
|
itemCount: products.length,
|
||||||
|
itemBuilder: (BuildContext context, int index) {
|
||||||
|
return Container(
|
||||||
|
height: 250,
|
||||||
|
child: ProductItemContainer(
|
||||||
|
product: products[index],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
staggeredTileBuilder: (int index) {
|
||||||
|
return StaggeredTile.fit(2);
|
||||||
|
},
|
||||||
|
mainAxisSpacing: 4.0,
|
||||||
|
crossAxisSpacing: 4.0,
|
||||||
|
)
|
||||||
|
: NoResultsForProductsWidget()),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
_onRefresh() async {
|
||||||
|
_productLoaderController.clear();
|
||||||
|
_shouldStopRequests = false;
|
||||||
|
|
||||||
|
await fetchProducts();
|
||||||
|
_refreshControllerUpsell.refreshCompleted();
|
||||||
|
}
|
||||||
|
|
||||||
|
_onLoading() async {
|
||||||
|
await fetchProducts();
|
||||||
|
|
||||||
|
if (mounted) {
|
||||||
|
setState(() {});
|
||||||
|
if (_shouldStopRequests) {
|
||||||
|
_refreshControllerUpsell.loadNoData();
|
||||||
|
} else {
|
||||||
|
_refreshControllerUpsell.loadComplete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future fetchProducts() async {
|
||||||
|
await _productLoaderController.loadProducts(
|
||||||
|
hasResults: (result) {
|
||||||
|
if (result == false) {
|
||||||
|
setState(() {
|
||||||
|
_shouldStopRequests = true;
|
||||||
|
});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
didFinish: () => setState(() {
|
||||||
|
_isLoading = false;
|
||||||
|
}),
|
||||||
|
productIds: widget.productIds);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,65 @@
|
|||||||
|
// Label StoreMax
|
||||||
|
//
|
||||||
|
// Created by Anthony Gordon.
|
||||||
|
// 2022, WooSignal Ltd. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_app/bootstrap/helpers.dart';
|
||||||
|
import 'package:flutter_rating_bar/flutter_rating_bar.dart';
|
||||||
|
import 'package:woosignal/models/response/product_review.dart';
|
||||||
|
|
||||||
|
class ProductReviewItemContainerWidget extends StatelessWidget {
|
||||||
|
const ProductReviewItemContainerWidget(
|
||||||
|
{Key key, @required this.productReview})
|
||||||
|
: super(key: key);
|
||||||
|
|
||||||
|
final ProductReview productReview;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Container(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
RatingBarIndicator(
|
||||||
|
rating: productReview.rating.toDouble(),
|
||||||
|
itemBuilder: (context, index) => Icon(
|
||||||
|
Icons.star,
|
||||||
|
color: Colors.amber,
|
||||||
|
),
|
||||||
|
itemCount: 5,
|
||||||
|
itemSize: 20.0,
|
||||||
|
direction: Axis.horizontal,
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(top: 8),
|
||||||
|
child: Text(parseHtmlString(productReview.review)),
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Text(productReview.reviewer),
|
||||||
|
Padding(
|
||||||
|
padding: EdgeInsets.symmetric(horizontal: 8),
|
||||||
|
child: Icon(
|
||||||
|
Icons.circle_rounded,
|
||||||
|
size: 3,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
formatDateTime("MMM d, yyyy").format(
|
||||||
|
parseDateTime(productReview.dateCreated),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -22,7 +22,6 @@ import 'package:flutter_app/resources/widgets/no_results_for_products_widget.dar
|
|||||||
import 'package:flutter_app/resources/widgets/top_nav_widget.dart';
|
import 'package:flutter_app/resources/widgets/top_nav_widget.dart';
|
||||||
import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart';
|
import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart';
|
||||||
import 'package:flutter_swiper/flutter_swiper.dart';
|
import 'package:flutter_swiper/flutter_swiper.dart';
|
||||||
import 'package:hexcolor/hexcolor.dart';
|
|
||||||
import 'package:nylo_support/helpers/helper.dart';
|
import 'package:nylo_support/helpers/helper.dart';
|
||||||
import 'package:pull_to_refresh/pull_to_refresh.dart';
|
import 'package:pull_to_refresh/pull_to_refresh.dart';
|
||||||
import 'package:woosignal/models/response/products.dart';
|
import 'package:woosignal/models/response/products.dart';
|
||||||
@ -303,7 +302,7 @@ class CheckoutMetaLine extends StatelessWidget {
|
|||||||
|
|
||||||
List<BoxShadow> wsBoxShadow({double blurRadius}) => [
|
List<BoxShadow> wsBoxShadow({double blurRadius}) => [
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
color: HexColor("#e8e8e8"),
|
color: Color(0xFFE8E8E8),
|
||||||
blurRadius: blurRadius ?? 15.0,
|
blurRadius: blurRadius ?? 15.0,
|
||||||
spreadRadius: 0,
|
spreadRadius: 0,
|
||||||
offset: Offset(
|
offset: Offset(
|
||||||
@ -452,7 +451,10 @@ class ProductItemContainer extends StatelessWidget {
|
|||||||
].where((e) => e != null).toList(),
|
].where((e) => e != null).toList(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
onTap: () => onTap(product),
|
onTap: () => onTap != null
|
||||||
|
? onTap(product)
|
||||||
|
: Navigator.pushNamed(context, "/product-detail",
|
||||||
|
arguments: product),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,9 +17,11 @@ import 'package:flutter_app/resources/pages/coupon_page.dart';
|
|||||||
import 'package:flutter_app/resources/pages/customer_countries.dart';
|
import 'package:flutter_app/resources/pages/customer_countries.dart';
|
||||||
import 'package:flutter_app/resources/pages/home.dart';
|
import 'package:flutter_app/resources/pages/home.dart';
|
||||||
import 'package:flutter_app/resources/pages/home_search.dart';
|
import 'package:flutter_app/resources/pages/home_search.dart';
|
||||||
|
import 'package:flutter_app/resources/pages/leave_review_page.dart';
|
||||||
import 'package:flutter_app/resources/pages/no_connection_page.dart';
|
import 'package:flutter_app/resources/pages/no_connection_page.dart';
|
||||||
import 'package:flutter_app/resources/pages/product_detail.dart';
|
import 'package:flutter_app/resources/pages/product_detail.dart';
|
||||||
import 'package:flutter_app/resources/pages/product_image_viewer_page.dart';
|
import 'package:flutter_app/resources/pages/product_image_viewer_page.dart';
|
||||||
|
import 'package:flutter_app/resources/pages/product_reviews_page.dart';
|
||||||
import 'package:flutter_app/resources/pages/wishlist_page_widget.dart';
|
import 'package:flutter_app/resources/pages/wishlist_page_widget.dart';
|
||||||
import 'package:flutter_app/resources/widgets/checkout_paypal.dart';
|
import 'package:flutter_app/resources/widgets/checkout_paypal.dart';
|
||||||
import 'package:nylo_support/router/router.dart';
|
import 'package:nylo_support/router/router.dart';
|
||||||
@ -47,6 +49,12 @@ appRouter() => nyRoutes((router) {
|
|||||||
router.route("/product-detail", (context) => ProductDetailPage(),
|
router.route("/product-detail", (context) => ProductDetailPage(),
|
||||||
transition: PageTransitionType.rightToLeftWithFade);
|
transition: PageTransitionType.rightToLeftWithFade);
|
||||||
|
|
||||||
|
router.route("/product-reviews", (context) => ProductReviewsPage(),
|
||||||
|
transition: PageTransitionType.rightToLeftWithFade);
|
||||||
|
|
||||||
|
router.route("/product-leave-review", (context) => LeaveReviewPage(),
|
||||||
|
transition: PageTransitionType.rightToLeftWithFade);
|
||||||
|
|
||||||
router.route("/product-images", (context) => ProductImageViewerPage(),
|
router.route("/product-images", (context) => ProductImageViewerPage(),
|
||||||
transition: PageTransitionType.fade);
|
transition: PageTransitionType.fade);
|
||||||
|
|
||||||
|
|||||||
@ -21,7 +21,7 @@ packages:
|
|||||||
name: animate_do
|
name: animate_do
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.0"
|
version: "2.1.0"
|
||||||
archive:
|
archive:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -43,6 +43,13 @@ packages:
|
|||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.8.2"
|
version: "2.8.2"
|
||||||
|
audio_session:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: audio_session
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "0.1.6+1"
|
||||||
auto_size_text:
|
auto_size_text:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -99,6 +106,13 @@ packages:
|
|||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.3.1"
|
version: "1.3.1"
|
||||||
|
chewie:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: chewie
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "1.2.2"
|
||||||
cli_util:
|
cli_util:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -249,6 +263,13 @@ packages:
|
|||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.0.3"
|
version: "0.0.3"
|
||||||
|
flutter_rating_bar:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: flutter_rating_bar
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "4.0.0"
|
||||||
flutter_secure_storage:
|
flutter_secure_storage:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -311,7 +332,7 @@ packages:
|
|||||||
name: flutter_stripe
|
name: flutter_stripe
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.0"
|
version: "2.1.1"
|
||||||
flutter_styled_toast:
|
flutter_styled_toast:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -319,6 +340,13 @@ packages:
|
|||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.0"
|
version: "2.0.0"
|
||||||
|
flutter_svg:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: flutter_svg
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.2"
|
||||||
flutter_swiper:
|
flutter_swiper:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -343,6 +371,20 @@ packages:
|
|||||||
description: flutter
|
description: flutter
|
||||||
source: sdk
|
source: sdk
|
||||||
version: "0.0.0"
|
version: "0.0.0"
|
||||||
|
flutter_widget_from_html:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: flutter_widget_from_html
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "0.8.4"
|
||||||
|
flutter_widget_from_html_core:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: flutter_widget_from_html_core
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "0.8.4"
|
||||||
freezed_annotation:
|
freezed_annotation:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -350,6 +392,62 @@ packages:
|
|||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.0"
|
version: "1.1.0"
|
||||||
|
fwfh_cached_network_image:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: fwfh_cached_network_image
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "0.7.0+2"
|
||||||
|
fwfh_chewie:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: fwfh_chewie
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "0.7.0+1"
|
||||||
|
fwfh_just_audio:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: fwfh_just_audio
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "0.6.2+2"
|
||||||
|
fwfh_selectable_text:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: fwfh_selectable_text
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "0.8.3+1"
|
||||||
|
fwfh_svg:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: fwfh_svg
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "0.7.2"
|
||||||
|
fwfh_text_style:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: fwfh_text_style
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "2.7.2"
|
||||||
|
fwfh_url_launcher:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: fwfh_url_launcher
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "0.6.1+3"
|
||||||
|
fwfh_webview:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: fwfh_webview
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "0.6.2+1"
|
||||||
glob:
|
glob:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -364,13 +462,6 @@ packages:
|
|||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.2.0"
|
version: "2.2.0"
|
||||||
hexcolor:
|
|
||||||
dependency: "direct main"
|
|
||||||
description:
|
|
||||||
name: hexcolor
|
|
||||||
url: "https://pub.dartlang.org"
|
|
||||||
source: hosted
|
|
||||||
version: "2.0.5"
|
|
||||||
html:
|
html:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -420,6 +511,27 @@ packages:
|
|||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.4.0"
|
version: "4.4.0"
|
||||||
|
just_audio:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: just_audio
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "0.9.18"
|
||||||
|
just_audio_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: just_audio_platform_interface
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "4.0.0"
|
||||||
|
just_audio_web:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: just_audio_web
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "0.4.2"
|
||||||
lints:
|
lints:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
@ -462,6 +574,13 @@ packages:
|
|||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.0.3"
|
version: "0.0.3"
|
||||||
|
nested:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: nested
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.0"
|
||||||
nylo_framework:
|
nylo_framework:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -503,7 +622,7 @@ packages:
|
|||||||
name: page_transition
|
name: page_transition
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.4"
|
version: "2.0.5"
|
||||||
path:
|
path:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -511,6 +630,20 @@ packages:
|
|||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.8.0"
|
version: "1.8.0"
|
||||||
|
path_drawing:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: path_drawing
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.0"
|
||||||
|
path_parsing:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: path_parsing
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.0"
|
||||||
path_provider:
|
path_provider:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -588,6 +721,13 @@ packages:
|
|||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.2.3"
|
version: "4.2.3"
|
||||||
|
provider:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: provider
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "5.0.0"
|
||||||
pub_semver:
|
pub_semver:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -622,7 +762,7 @@ packages:
|
|||||||
name: shared_preferences
|
name: shared_preferences
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.11"
|
version: "2.0.12"
|
||||||
shared_preferences_android:
|
shared_preferences_android:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -732,21 +872,21 @@ packages:
|
|||||||
name: stripe_android
|
name: stripe_android
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.0"
|
version: "2.1.1"
|
||||||
stripe_ios:
|
stripe_ios:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: stripe_ios
|
name: stripe_ios
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.0"
|
version: "2.1.1"
|
||||||
stripe_platform_interface:
|
stripe_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: stripe_platform_interface
|
name: stripe_platform_interface
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.0"
|
version: "2.1.1"
|
||||||
synchronized:
|
synchronized:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -795,7 +935,7 @@ packages:
|
|||||||
name: url_launcher
|
name: url_launcher
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.0.17"
|
version: "6.0.18"
|
||||||
url_launcher_android:
|
url_launcher_android:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -866,6 +1006,62 @@ packages:
|
|||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.1"
|
version: "2.1.1"
|
||||||
|
video_player:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: video_player
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "2.2.13"
|
||||||
|
video_player_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: video_player_platform_interface
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "5.0.0"
|
||||||
|
video_player_web:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: video_player_web
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.6"
|
||||||
|
wakelock:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: wakelock
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "0.5.6"
|
||||||
|
wakelock_macos:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: wakelock_macos
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "0.4.0"
|
||||||
|
wakelock_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: wakelock_platform_interface
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "0.3.0"
|
||||||
|
wakelock_web:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: wakelock_web
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "0.4.0"
|
||||||
|
wakelock_windows:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: wakelock_windows
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "0.2.0"
|
||||||
watcher:
|
watcher:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -914,7 +1110,7 @@ packages:
|
|||||||
name: woosignal
|
name: woosignal
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.3"
|
version: "3.0.5"
|
||||||
wp_json_api:
|
wp_json_api:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -945,4 +1141,4 @@ packages:
|
|||||||
version: "3.1.0"
|
version: "3.1.0"
|
||||||
sdks:
|
sdks:
|
||||||
dart: ">=2.15.0 <3.0.0"
|
dart: ">=2.15.0 <3.0.0"
|
||||||
flutter: ">=2.5.0"
|
flutter: ">=2.8.0"
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
# Official WooSignal App Template for WooCommerce
|
# Official WooSignal App Template for WooCommerce
|
||||||
|
|
||||||
# Label StoreMax
|
# Label StoreMax
|
||||||
# Version: 5.6.2
|
# Version: 5.7.0
|
||||||
# Author: Anthony Gordon
|
# Author: Anthony Gordon
|
||||||
# Homepage: https://woosignal.com
|
# Homepage: https://woosignal.com
|
||||||
# Documentation: https://woosignal.com/docs/app/label-storemax
|
# Documentation: https://woosignal.com/docs/app/label-storemax
|
||||||
@ -28,10 +28,10 @@ dependencies:
|
|||||||
google_fonts: ^2.2.0
|
google_fonts: ^2.2.0
|
||||||
analyzer: ^1.5.0
|
analyzer: ^1.5.0
|
||||||
intl: ^0.17.0
|
intl: ^0.17.0
|
||||||
page_transition: ^2.0.4
|
page_transition: ^2.0.5
|
||||||
nylo_framework: ^2.1.4
|
nylo_framework: ^2.1.4
|
||||||
woosignal: ^3.0.3
|
woosignal: ^3.0.5
|
||||||
flutter_stripe: ^2.1.0
|
flutter_stripe: ^2.1.1
|
||||||
wp_json_api: ^3.1.3
|
wp_json_api: ^3.1.3
|
||||||
cached_network_image: ^3.2.0
|
cached_network_image: ^3.2.0
|
||||||
package_info: ^2.0.2
|
package_info: ^2.0.2
|
||||||
@ -41,17 +41,18 @@ dependencies:
|
|||||||
webview_flutter: ^2.3.1
|
webview_flutter: ^2.3.1
|
||||||
pull_to_refresh: 2.0.0
|
pull_to_refresh: 2.0.0
|
||||||
flutter_swiper: ^1.1.6
|
flutter_swiper: ^1.1.6
|
||||||
url_launcher: ^6.0.17
|
url_launcher: ^6.0.18
|
||||||
flutter_styled_toast: ^2.0.0
|
flutter_styled_toast: ^2.0.0
|
||||||
animate_do: ^2.0.0
|
animate_do: ^2.1.0
|
||||||
bubble_tab_indicator: ^0.1.5
|
bubble_tab_indicator: ^0.1.5
|
||||||
status_alert: ^0.1.3
|
status_alert: ^0.1.3
|
||||||
math_expressions: ^2.3.0
|
math_expressions: ^2.3.0
|
||||||
validated: ^2.0.0
|
validated: ^2.0.0
|
||||||
hexcolor: ^2.0.5
|
|
||||||
flutter_spinkit: ^5.1.0
|
flutter_spinkit: ^5.1.0
|
||||||
auto_size_text: ^3.0.0
|
auto_size_text: ^3.0.0
|
||||||
html: ^0.15.0
|
html: ^0.15.0
|
||||||
|
flutter_widget_from_html: ^0.8.4
|
||||||
|
flutter_rating_bar: ^4.0.0
|
||||||
flutter_staggered_grid_view: ^0.4.1
|
flutter_staggered_grid_view: ^0.4.1
|
||||||
# firebase_messaging: ^11.2.3
|
# firebase_messaging: ^11.2.3
|
||||||
# firebase_core: ^1.10.5
|
# firebase_core: ^1.10.5
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user