v6.6.0 updates

This commit is contained in:
Anthony 2023-05-18 19:46:04 +01:00
parent 004c146967
commit da2301a2af
38 changed files with 269 additions and 1523 deletions

View File

@ -49,4 +49,6 @@ RAZORPAY_API_KEY=""
# *<! ------ EXTRAS ------!>*
PRODUCT_PLACEHOLDER_IMAGE="https://woosignal.com/images/woocommerce-placeholder.png"
# Sets the default placeholder image for products with no image
# Sets the default placeholder image for products with no image
AUTH_USER_KEY="AUTH_USER"

View File

@ -1,3 +1,9 @@
## [6.6.0] - 2023-05-18
* Nylo v5.0.0 migration
* Refactor project
* Flutter v3.10.0 compatibility
## [6.5.1] - 2023-03-04
* New translation added.

View File

@ -4,7 +4,7 @@
# WooCommerce App: Label StoreMax
### Label StoreMax - v6.5.1
### Label StoreMax - v6.6.0
[Official WooSignal WooCommerce App](https://woosignal.com)
@ -44,7 +44,7 @@ Full documentation this available [here](https://woosignal.com/docs/app/label-st
- Browse products, make orders, customer login (via WordPress)
- Change app name, logo, customize default language, currency + more
- Light and dark mode
- Stripe, Cash On Delivery, PayPal, RazorPay
- Stripe, Cash On Delivery, PayPal
- Localized for en, es, pt, it, hi, fr, zh, tr, nl, de
- Orders show as normal in WooCommerce

View File

@ -204,6 +204,7 @@
files = (
);
inputPaths = (
"${TARGET_BUILD_DIR}/${INFOPLIST_PATH}",
);
name = "Thin Binary";
outputPaths = (

View File

@ -2,6 +2,8 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CADisableMinimumFrameDurationOnPhone</key>
<true/>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
@ -16,12 +18,8 @@
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>$(FLUTTER_BUILD_NAME)</string>
<key>NSCameraUsageDescription</key>
<string>You can take photos of your payment details.</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>MinimumOSVersion</key>
<string>13.0</string>
<key>CFBundleURLTypes</key>
<array>
<dict>
@ -39,11 +37,17 @@
<string>$(FLUTTER_BUILD_NUMBER)</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>MinimumOSVersion</key>
<string>13.0</string>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
<key>NSCameraUsageDescription</key>
<string>You can take photos of your payment details.</string>
<key>UIApplicationSupportsIndirectInputEvents</key>
<true/>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
@ -61,9 +65,5 @@
</array>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
<key>CADisableMinimumFrameDurationOnPhone</key>
<true/>
<key>UIApplicationSupportsIndirectInputEvents</key>
<true/>
</dict>
</plist>

View File

@ -10,18 +10,16 @@
import 'package:nylo_framework/nylo_framework.dart';
class User extends Storable {
class User extends Model {
String? userId;
String? token;
User();
User.fromUserAuthResponse({this.userId, this.token});
@override
toStorage() => {"token": token, "user_id": userId};
toJson() => {"token": token, "user_id": userId};
@override
fromStorage(dynamic data) {
fromJson(dynamic data) {
token = data['token'];
userId = data['user_id'];
}

View File

@ -8,7 +8,7 @@ import 'package:nylo_framework/nylo_framework.dart';
| ApiService
| -------------------------------------------------------------------------
| Define your API endpoints
| Learn more https://nylo.dev/docs/4.x/networking
| Learn more https://nylo.dev/docs/5.x/networking
|--------------------------------------------------------------------------
*/

View File

@ -4,8 +4,10 @@ import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_app/bootstrap/app_helper.dart';
import 'package:flutter_app/bootstrap/helpers.dart';
import 'package:flutter_app/config/decoders.dart';
import 'package:flutter_app/config/design.dart';
import 'package:flutter_app/config/theme.dart';
import 'package:flutter_app/config/validation_rules.dart';
import 'package:nylo_framework/nylo_framework.dart';
import 'package:flutter_app/config/localization.dart';
import 'package:woosignal/models/response/woosignal_app.dart';
@ -112,12 +114,14 @@ class AppProvider implements NyProvider {
nylo.appLoader = loader;
nylo.appLogo = logo;
String initialRoute = AppHelper.instance.appConfig!.appStatus != null
? '/home'
: '/no-connection';
nylo.initialRoute = initialRoute;
nylo.addModelDecoders(modelDecoders);
nylo.addValidationRules(validationRules);
return nylo;
}
@override
afterBoot(Nylo nylo) async {
}
}

View File

@ -8,4 +8,9 @@ class EventProvider implements NyProvider {
return nylo;
}
@override
afterBoot(Nylo nylo) async {
}
}

View File

@ -1,81 +0,0 @@
//
// LabelCore
// Label StoreMAX
//
// Created by Anthony Gordon.
// 2023, 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/widgets.dart';
import 'package:flutter_app/app/models/cart.dart';
import 'package:flutter_app/bootstrap/data/order_wc.dart';
import 'package:flutter_app/bootstrap/helpers.dart';
import 'package:flutter_app/resources/pages/checkout_confirmation_page.dart';
import 'package:nylo_framework/nylo_framework.dart';
import 'package:razorpay_flutter/razorpay_flutter.dart';
import 'package:woosignal/models/response/tax_rate.dart';
import 'package:woosignal/models/payload/order_wc.dart';
import 'package:woosignal/models/response/order.dart';
razorPay(context,
{required CheckoutConfirmationPageState state, TaxRate? taxRate}) async {
Razorpay razorpay = Razorpay();
razorpay.on(Razorpay.EVENT_PAYMENT_SUCCESS,
(PaymentSuccessResponse response) async {
OrderWC orderWC = await buildOrderWC(taxRate: taxRate);
Order? order = await appWooSignal((api) => api.createOrder(orderWC));
if (order != null) {
Cart.getInstance.clear();
Navigator.pushNamed(context, "/checkout-status", arguments: order);
} else {
showToastNotification(
context,
title: "Error".tr(),
description: trans("Something went wrong, please contact our store"),
);
state.reloadState(showLoader: false);
}
});
razorpay.on(Razorpay.EVENT_PAYMENT_ERROR, (PaymentFailureResponse response) {
showToastNotification(context,
title: trans("Error"),
description: response.message ?? "",
style: ToastNotificationStyleType.WARNING);
state.reloadState(showLoader: false);
});
razorpay.on(Razorpay.EVENT_EXTERNAL_WALLET, _handleExternalWallet);
// CHECKOUT HELPER
await checkout(taxRate, (total, billingDetails, cart) async {
var options = {
'key': getEnv('RAZORPAY_API_KEY'),
'amount': (double.parse(total) * 100).toInt(),
'name': getEnv('APP_NAME'),
'description': await cart.cartShortDesc(),
'prefill': {
"name": [
billingDetails!.billingAddress?.firstName,
billingDetails.billingAddress?.lastName
].where((t) => t != null || t != "").toList().join(" "),
"method": "card",
'email': billingDetails.billingAddress?.emailAddress ?? ""
}
};
state.reloadState(showLoader: true);
razorpay.open(options);
});
}
void _handleExternalWallet(ExternalWalletResponse response) {}

View File

@ -1,3 +1,4 @@
import 'package:flutter_app/bootstrap/app_helper.dart';
import 'package:flutter_app/routes/router.dart';
import 'package:nylo_framework/nylo_framework.dart';
@ -8,4 +9,12 @@ class RouteProvider implements NyProvider {
return nylo;
}
@override
afterBoot(Nylo nylo) async {
String initialRoute = AppHelper.instance.appConfig!.appStatus != null
? '/home'
: '/no-connection';
nylo.setInitialRoute(initialRoute);
}
}

View File

@ -1,12 +1,8 @@
// import 'package:firebase_core/firebase_core.dart';
// import 'package:firebase_messaging/firebase_messaging.dart';
// import 'package:flutter_app/firebase_options.dart';
/// boot application
import 'package:flutter_app/config/providers.dart';
import 'package:nylo_framework/nylo_framework.dart';
class Boot {
static Future<Nylo> nylo() async => await bootApplication(providers);
static Future<void> finished(Nylo nylo) async => await bootFinished(nylo);
static Future<void> finished(Nylo nylo) async => await bootFinished(nylo, providers);
}

View File

@ -138,3 +138,37 @@ extension NyText on Text {
style: style != null ? this.style?.merge(style) ?? style : this.style);
}
}
/// Check if the [Product] is new.
extension DateTimeExtension on DateTime? {
bool? isAfterOrEqualTo(DateTime dateTime) {
final date = this;
if (date != null) {
final isAtSameMomentAs = dateTime.isAtSameMomentAs(date);
return isAtSameMomentAs | date.isAfter(dateTime);
}
return null;
}
bool? isBeforeOrEqualTo(DateTime dateTime) {
final date = this;
if (date != null) {
final isAtSameMomentAs = dateTime.isAtSameMomentAs(date);
return isAtSameMomentAs | date.isBefore(dateTime);
}
return null;
}
bool? isBetween(
DateTime fromDateTime,
DateTime toDateTime,
) {
final date = this;
if (date != null) {
final isAfter = date.isAfterOrEqualTo(fromDateTime) ?? false;
final isBefore = date.isBeforeOrEqualTo(toDateTime) ?? false;
return isAfter && isBefore;
}
return null;
}
}

View File

@ -20,6 +20,7 @@ import 'package:flutter_app/app/models/payment_type.dart';
import 'package:flutter_app/app/models/user.dart';
import 'package:flutter_app/bootstrap/app_helper.dart';
import 'package:flutter_app/bootstrap/enums/symbol_position_enums.dart';
import 'package:flutter_app/bootstrap/extensions.dart';
import 'package:flutter_app/bootstrap/shared_pref/shared_key.dart';
import 'package:flutter_app/config/currency.dart';
import 'package:flutter_app/config/decoders.dart';
@ -45,7 +46,7 @@ import '../resources/themes/styles/color_styles.dart';
import 'package:flutter/services.dart' show rootBundle;
Future<User?> getUser() async =>
(await (NyStorage.read<User>(SharedKey.authUser, model: User())));
(await (NyStorage.read<User>(SharedKey.authUser)));
Future appWooSignal(Function(WooSignal) api) async {
return await api(WooSignal.instance);
@ -156,6 +157,7 @@ String moneyFormatter(double amount) {
amount: amount,
settings: MoneyFormatterSettings(
symbol: AppHelper.instance.appConfig!.currencyMeta!.symbolNative,
symbolAndNumberSeparator: ""
),
);
if (appCurrencySymbolPosition == SymbolPositionType.left) {
@ -486,7 +488,7 @@ Widget refreshableScroll(context,
return StaggeredGridTile.fit(
crossAxisCellCount: 1,
child: Container(
height: 200,
height: 350,
child: ProductItemContainer(
product: product,
onTap: onTap,
@ -654,3 +656,15 @@ api<T>(dynamic Function(T) request, {BuildContext? context}) async =>
/// Event helper
event<T>({Map? data}) async => nyEvent<T>(params: data, events: events);
/// Check if the [Product] is new.
bool isProductNew(Product? product) {
if (product?.dateCreatedGMT == null) false;
try {
DateTime dateTime = DateTime.parse(product!.dateCreatedGMT!);
return dateTime.isBetween(DateTime.now().subtract(Duration(days: 2)), DateTime.now()) ?? false;
} on Exception catch (e) {
NyLogger.error(e.toString());
}
return false;
}

View File

@ -6,7 +6,7 @@ import 'package:flutter_app/app/networking/dio/base_api_service.dart';
| Model Decoders
| -------------------------------------------------------------------------
| Model decoders are used in 'app/networking/' for morphing json payloads
| into Models. Learn more https://nylo.dev/docs/4.x/decoders#model-decoders
| into Models. Learn more https://nylo.dev/docs/5.x/decoders#model-decoders
|--------------------------------------------------------------------------
*/
@ -20,7 +20,7 @@ final Map<Type, dynamic> modelDecoders = {
| -------------------------------------------------------------------------
| API decoders are used when you need to access an API service using the
| 'api' helper. E.g. api<MyApiService>((request) => request.fetchData());
| Learn more https://nylo.dev/docs/4.x/decoders#api-decoders
| Learn more https://nylo.dev/docs/5.x/decoders#api-decoders
|--------------------------------------------------------------------------
*/

View File

@ -7,7 +7,7 @@ import 'package:flutter_app/resources/widgets/woosignal_ui.dart';
| Design
| Contains widgets used in the Nylo framework.
|
| Learn more: https://nylo.dev/docs/4.x/themes
| Learn more: https://nylo.dev/docs/5.x/themes
|--------------------------------------------------------------------------
*/

View File

@ -8,7 +8,7 @@ import 'package:nylo_framework/nylo_framework.dart';
| Add your "app/events" here.
| Events can be fired using: event<MyEvent>();
|
| Learn more: https://nylo.dev/docs/4.x/events
| Learn more: https://nylo.dev/docs/5.x/events
|--------------------------------------------------------------------------
*/

View File

@ -1,7 +1,6 @@
import 'package:flutter_app/app/models/payment_type.dart';
import 'package:flutter_app/app/providers/payments/cash_on_delivery.dart';
import 'package:flutter_app/app/providers/payments/paypal_pay.dart';
import 'package:flutter_app/app/providers/payments/razorpay_pay.dart';
import 'package:flutter_app/app/providers/payments/stripe_pay.dart';
import 'package:flutter_app/bootstrap/helpers.dart';
import 'package:nylo_framework/nylo_framework.dart';
@ -44,14 +43,6 @@ List<PaymentType> paymentTypeList = [
pay: payPalPay,
),
addPayment(
id: 5,
name: "RazorPay",
description: trans("Debit or Credit Card"),
assetImage: "razorpay.png",
pay: razorPay,
),
// e.g. add more here
// addPayment(

View File

@ -9,7 +9,7 @@ import 'package:nylo_framework/nylo_framework.dart';
| Add your "app/providers" here.
| Providers are booted when your application start.
|
| Learn more: https://nylo.dev/docs/4.x/providers
| Learn more: https://nylo.dev/docs/5.x/providers
|--------------------------------------------------------------------------
*/

View File

@ -5,12 +5,15 @@
| E.g. static String userCoins = "USER_COINS";
| String coins = NyStorage.read( StorageKey.userCoins );
|
| Learn more: https://nylo.dev/docs/4.x/storage#storage-keys
| Learn more: https://nylo.dev/docs/5.x/storage#storage-keys
|--------------------------------------------------------------------------
*/
import 'package:nylo_framework/nylo_framework.dart';
class StorageKey {
static String userToken = "USER_TOKEN";
static String authUser = getEnv('AUTH_USER_KEY', defaultValue: 'AUTH_USER');
/// Add your storage keys here...
}

View File

@ -0,0 +1,37 @@
import 'package:nylo_framework/nylo_framework.dart';
/*
|--------------------------------------------------------------------------
| Validation Rules
| -------------------------------------------------------------------------
| Add custom validation rules for your project in this file.
| Learn more https://nylo.dev/docs/5.x/validation#custom-validation-rules
|--------------------------------------------------------------------------
*/
final Map<Type, dynamic> validationRules = {
/// Example
// SimplePassword: (attribute) => SimplePassword(attribute)
};
/// Example validation class
// class SimplePassword extends ValidationRule {
// SimplePassword(String attribute)
// : super(
// attribute: attribute,
// signature: "simple_password", // Use this signature for the validator
// description: "The $attribute field must be between 4 and 8 digits long and include at least one numeric digit", // Toast description when an error occurs
// textFieldMessage: "Must be between 4 and 8 digits long with one numeric digit"); // TextField description when an error occurs
//
// @override
// handle(Map<String, dynamic> info) {
// super.handle(info);
//
// /// info['rule'] = Validation rule i.e "min".
// /// info['data'] = Data the user has passed into the validation.
// /// info['message'] = Overriding message to be displayed for validation (optional).
//
// RegExp regExp = RegExp(r'^(?=.*\d).{4,8}$');
// return regExp.hasMatch(info['data']);
// }
// }

View File

@ -11,7 +11,7 @@ void main() async {
AppBuild(
navigatorKey: NyNavigator.instance.router.navigatorKey,
onGenerateRoute: nylo.router!.generator(),
initialRoute: nylo.initialRoute,
initialRoute: nylo.getInitialRoute(),
debugShowCheckedModeBanner: false,
),
);

View File

@ -35,11 +35,6 @@ class _AccountLandingPageState extends NyState<AccountLandingPage> {
final TextEditingController _tfEmailController = TextEditingController(),
_tfPasswordController = TextEditingController();
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
@ -231,7 +226,7 @@ class _AccountLandingPageState extends NyState<AccountLandingPage> {
String? token = wpUserLoginResponse.data!.userToken;
String userId = wpUserLoginResponse.data!.userId.toString();
User user = User.fromUserAuthResponse(token: token, userId: userId);
await user.save(SharedKey.authUser);
await Auth.set(user, key: SharedKey.authUser);
showToastNotification(context,
title: trans("Hello"),

View File

@ -48,11 +48,6 @@ class _AccountRegistrationPageState extends NyState<AccountRegistrationPage> {
final WooSignalApp? _wooSignalApp = AppHelper.instance.appConfig;
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
@ -241,7 +236,7 @@ class _AccountRegistrationPageState extends NyState<AccountRegistrationPage> {
String? token = wpUserRegisterResponse.data!.userToken;
String userId = wpUserRegisterResponse.data!.userId.toString();
User user = User.fromUserAuthResponse(token: token, userId: userId);
await user.save(SharedKey.authUser);
await Auth.set(user, key: SharedKey.authUser);
await WPJsonAPI.instance.api((request) => request.wpUpdateUserInfo(token,
firstName: firstName, lastName: lastName));

View File

@ -85,7 +85,8 @@ class _BrowseCategoryPageState extends NyState<BrowseCategoryPage> {
onRefresh: _onRefresh,
onLoading: _onLoading,
products: _productCategorySearchLoaderController.getResults(),
onTap: _showProduct),
onTap: _showProduct,
),
),
);
}

View File

@ -135,42 +135,41 @@ class _LeaveReviewPageState extends NyState<LeaveReviewPage> {
return;
}
try {
validator(rules: {"review": "min:5"}, data: {"review": review});
ProductReview? productReview =
await validate(
rules: {"review": "min:5"},
data: {"review": review},
onSuccess: () async {
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,
)));
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;
});
}
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);
});
setState(() {
_isLoading = false;
});
}
Future<wc_customer_info.Data?> _fetchWpUserData() async {

View File

@ -17,7 +17,6 @@ import 'package:flutter_app/bootstrap/helpers.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/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';
@ -232,9 +231,9 @@ class _ProductDetailState extends NyState<ProductDetailPage> {
appBar: AppBar(
actions: <Widget>[
if (_wooSignalApp!.wishlistEnabled!)
FutureBuildWidget(
asyncFuture: hasAddedWishlistProduct(_product!.id),
onValue: (dynamic isInFavourites) {
NyFutureBuilder(
future: hasAddedWishlistProduct(_product!.id),
child: (context, dynamic isInFavourites) {
return isInFavourites
? IconButton(
onPressed: () => widget.controller.toggleWishList(

View File

@ -29,6 +29,7 @@ ThemeData lightTheme(ColorStyles lightColors) {
getAppTextTheme(appFont, defaultTextTheme.merge(_textTheme(lightColors)));
return ThemeData(
useMaterial3: true,
primaryColor: lightColors.primaryContent,
primaryColorLight: lightColors.primaryAccent,
focusColor: lightColors.primaryContent,

View File

@ -10,7 +10,7 @@
import 'package:flutter/material.dart';
import 'package:nylo_framework/nylo_framework.dart';
import 'package:package_info/package_info.dart';
import 'package:package_info_plus/package_info_plus.dart';
class AppVersionWidget extends StatelessWidget {
const AppVersionWidget({Key? key}) : super(key: key);

View File

@ -23,45 +23,48 @@ class CartIconWidget extends StatefulWidget {
class _CartIconWidgetState extends State<CartIconWidget> {
@override
Widget build(BuildContext context) {
return IconButton(
icon: Stack(
children: <Widget>[
Positioned.fill(
child: Align(
child: Icon(Icons.shopping_cart, size: 20),
alignment: Alignment.bottomCenter,
),
bottom: 0,
),
Positioned.fill(
child: Align(
child: NyFutureBuilder<List<CartLineItem>>(
future: Cart.getInstance.getCart(),
child: (BuildContext context,
data) {
List<int?> cartItems =
data.map((e) => e.quantity).toList();
String cartValue = "0";
if (cartItems.isNotEmpty) {
cartValue = cartItems
.reduce((value, element) => value! + element!)
.toString();
}
return Text(
cartValue,
style: Theme.of(context).textTheme.bodyMedium,
textAlign: TextAlign.center,
);
},
return Container(
width: 70,
child: IconButton(
icon: Stack(
children: <Widget>[
Positioned.fill(
child: Align(
child: Icon(Icons.shopping_cart, size: 20),
alignment: Alignment.bottomCenter,
),
alignment: Alignment.topCenter,
bottom: 0,
),
top: 0,
)
],
Positioned.fill(
child: Align(
child: NyFutureBuilder<List<CartLineItem>>(
future: Cart.getInstance.getCart(),
child: (BuildContext context,
data) {
List<int?> cartItems =
data.map((e) => e.quantity).toList();
String cartValue = "0";
if (cartItems.isNotEmpty) {
cartValue = cartItems
.reduce((value, element) => value! + element!)
.toString();
}
return Text(
cartValue,
style: Theme.of(context).textTheme.bodyMedium,
textAlign: TextAlign.center,
);
},
),
alignment: Alignment.topCenter,
),
top: 0,
)
],
),
onPressed: () => Navigator.pushNamed(context, "/cart")
.then((value) => setState(() {})),
),
onPressed: () => Navigator.pushNamed(context, "/cart")
.then((value) => setState(() {})),
);
}
}

View File

@ -1,43 +0,0 @@
// Label StoreMax
//
// Created by Anthony Gordon.
// 2023, 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.then((value) => value as T),
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);
}
}
},
);
}
}

View File

@ -10,8 +10,6 @@
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';
@ -53,10 +51,10 @@ class ProductDetailRelatedProductsWidget extends StatelessWidget {
),
),
Container(
height: 200,
child: FutureBuildWidget<List<Product>>(
asyncFuture: fetchRelated(),
onValue: (relatedProducts) {
height: 300,
child: NyFutureBuilder<List<Product>>(
future: fetchRelated(),
child: (context, relatedProducts) {
if (relatedProducts == null) {
return SizedBox.shrink();
}
@ -70,7 +68,6 @@ class ProductDetailRelatedProductsWidget extends StatelessWidget {
.toList(),
);
},
onLoading: AppLoaderWidget(),
),
),
],

View File

@ -12,7 +12,6 @@ import 'package:auto_size_text/auto_size_text.dart';
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';
@ -91,12 +90,9 @@ class _ProductDetailReviewsWidgetState
initiallyExpanded: false,
children: [
if (_ratingExpanded == true)
FutureBuildWidget<List<ProductReview>>(
asyncFuture: fetchReviews(),
onValue: (reviews) {
if (reviews == null) {
return SizedBox.shrink();
}
NyFutureBuilder<List<ProductReview>>(
future: fetchReviews(),
child: (context, reviews) {
int reviewsCount = reviews.length;
List<Widget> childrenWidgets = [];
List<ProductDetailReviewTileWidget> children = reviews
@ -137,7 +133,7 @@ class _ProductDetailReviewsWidgetState
: childrenWidgets,
);
},
onLoading: Padding(
loading: Padding(
padding: const EdgeInsets.symmetric(vertical: 8),
child: CupertinoActivityIndicator(),
),
@ -149,9 +145,9 @@ class _ProductDetailReviewsWidgetState
}
Future<List<ProductReview>> fetchReviews() async {
return await (appWooSignal(
return await appWooSignal(
(api) => api.getProductReviews(
perPage: 5, product: [widget.product!.id!], status: "approved"),
));
);
}
}

View File

@ -75,7 +75,7 @@ class _ProductDetailUpsellWidgetState extends State<ProductDetailUpsellWidget> {
),
),
Container(
height: 200,
height: 300,
child: ListView(
shrinkWrap: true,
scrollDirection: Axis.horizontal,

View File

@ -315,6 +315,7 @@ class ProductItemContainer extends StatelessWidget {
if (product == null) {
return SizedBox.shrink();
}
return LayoutBuilder(
builder: (cxt, constraints) => InkWell(
child: Container(
@ -324,7 +325,7 @@ class ProductItemContainer extends StatelessWidget {
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Container(
height: constraints.maxHeight / 2,
height: constraints.maxHeight / 1.6,
child: ClipRRect(
borderRadius: BorderRadius.circular(3.0),
child: Stack(
@ -338,10 +339,12 @@ class ProductItemContainer extends StatelessWidget {
image: (product!.images.isNotEmpty
? product!.images.first.src
: getEnv("PRODUCT_PLACEHOLDER_IMAGE")),
fit: BoxFit.contain,
height: constraints.maxHeight / 2,
fit: BoxFit.cover,
height: constraints.maxHeight / 1.6,
width: double.infinity,
),
if (isProductNew(product))
Container(padding: EdgeInsets.all(4), child: Text("New", style: TextStyle(color: Colors.white),), decoration: BoxDecoration(color: Colors.black),),
if (product!.onSale! && product!.type != "variable")
Positioned(
bottom: 0,
@ -351,7 +354,7 @@ class ProductItemContainer extends StatelessWidget {
padding: EdgeInsets.all(3),
decoration: BoxDecoration(
color: Colors.white70,
borderRadius: BorderRadius.circular(4),
// borderRadius: BorderRadius.circular(4),
),
child: RichText(
textAlign: TextAlign.center,
@ -366,8 +369,8 @@ class ProductItemContainer extends StatelessWidget {
.textTheme
.bodyLarge!
.copyWith(
color: Colors.black87,
fontSize: 11,
color: Colors.black,
fontSize: 13,
),
),
],
@ -393,16 +396,17 @@ class ProductItemContainer extends StatelessWidget {
),
Flexible(
child: Container(
padding: EdgeInsets.only(top: 4),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.start,
children: [
AutoSizeText(
"${formatStringCurrency(total: product!.price)} ",
style: Theme.of(context)
.textTheme
.bodyMedium!
.copyWith(fontWeight: FontWeight.w600),
.bodyLarge!
.copyWith(fontWeight: FontWeight.w800),
textAlign: TextAlign.left,
),
if (product!.onSale! && product!.type != "variable")

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,7 @@
# Official WooSignal App Template for WooCommerce
# Label StoreMax
# Version: 6.5.1
# Version: 6.6.0
# Author: Anthony Gordon
# Homepage: https://woosignal.com
# Documentation: https://woosignal.com/docs/app/label-storemax
@ -22,27 +22,29 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
version: 1.0.0+1
environment:
sdk: '>=2.19.0 <3.0.0'
sdk: ">=2.17.0 <3.0.0"
flutter: ">=3.0.0"
dependencies:
google_fonts: ^4.0.3
analyzer: ^4.2.0
intl: ^0.17.0
nylo_framework: ^4.1.4
woosignal: ^3.3.0
flutter_stripe: ^8.0.0+1
analyzer: ^5.12.0
intl: ^0.18.0
nylo_framework: ^5.0.0
# woosignal: ^3.3.0
woosignal:
path: /Users/anthony/StudioProjects/woosignal-api
# flutter_stripe: ^9.2.0
wp_json_api: ^3.3.2
cached_network_image: ^3.2.3
package_info: ^2.0.2
package_info_plus: ^4.0.0
money_formatter: ^0.0.3
flutter_web_browser: ^0.17.1
webview_flutter: ^3.0.4
pull_to_refresh_flutter3: 2.0.1
url_launcher: ^6.1.6
bubble_tab_indicator: ^0.1.5
razorpay_flutter: ^1.3.4
status_alert: ^1.0.1
math_expressions: ^2.3.1
math_expressions: ^2.4.0
validated: ^2.0.0
flutter_spinkit: ^5.1.0
auto_size_text: ^3.0.0
@ -51,8 +53,8 @@ dependencies:
flutter_rating_bar: ^4.0.1
flutter_staggered_grid_view: ^0.6.2
flutter_swiper_view: ^1.1.8
firebase_messaging: ^14.2.5
firebase_core: ^2.7.0
firebase_messaging: ^14.4.1
firebase_core: ^2.10.0
flutter:
sdk: flutter
flutter_localizations:
@ -62,15 +64,16 @@ dependencies:
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^1.0.5
collection: ^1.15.0
flutter_stripe: ^9.1.1
dev_dependencies:
flutter_launcher_icons: ^0.12.0
flutter_launcher_icons: ^0.13.1
lints: ^2.0.0
flutter_test:
sdk: flutter
# APP ICON
flutter_icons:
flutter_launcher_icons:
android: true
ios: true
image_path: "public/assets/app_icon/appicon.png"

View File

@ -4,7 +4,7 @@
# WooCommerce App: Label StoreMax
### Label StoreMax - v6.5.1
### Label StoreMax - v6.6.0
[Official WooSignal WooCommerce App](https://woosignal.com)
@ -45,7 +45,7 @@ Full documentation this available [here](https://woosignal.com/docs/app/ios/labe
- Change app name, logo, customize default language, currency + more
- Light and dark mode
- Theme customization
- Stripe, Cash On Delivery, PayPal and RazorPay
- Stripe, Cash On Delivery, PayPal
- Localized for en, es, pt, it, hi, fr, zh, tr, nl
- Orders show as normal in WooCommerce