diff --git a/android/gradle.properties b/android/gradle.properties
index 8df3fe9..acecd2f 100644
--- a/android/gradle.properties
+++ b/android/gradle.properties
@@ -4,4 +4,8 @@ android.useAndroidX=true
android.enableJetifier=true
apmsInstrumentationEnabled=false
-#org.gradle.java.home=/Users/amirs/Library/Java/JavaVirtualMachines/jbr-17.0.12/Contents/Home/
\ No newline at end of file
+#org.gradle.java.home=/Users/amirs/Library/Java/JavaVirtualMachines/jbr-17.0.12/Contents/Home/
+# This builtInKotlin flag was added automatically by Flutter migrator
+android.builtInKotlin=false
+# This newDsl flag was added automatically by Flutter migrator
+android.newDsl=false
diff --git a/assets/icons/duration.svg b/assets/icons/duration.svg
new file mode 100644
index 0000000..ba3daa0
--- /dev/null
+++ b/assets/icons/duration.svg
@@ -0,0 +1,4 @@
+
diff --git a/assets/icons/location.svg b/assets/icons/location.svg
new file mode 100644
index 0000000..355aca8
--- /dev/null
+++ b/assets/icons/location.svg
@@ -0,0 +1,5 @@
+
diff --git a/assets/icons/store.svg b/assets/icons/store.svg
new file mode 100644
index 0000000..5cd8fdb
--- /dev/null
+++ b/assets/icons/store.svg
@@ -0,0 +1,6 @@
+
diff --git a/assets/langs/ar-SA.json b/assets/langs/ar-SA.json
index 7878074..ee32173 100644
--- a/assets/langs/ar-SA.json
+++ b/assets/langs/ar-SA.json
@@ -711,6 +711,7 @@
"riyadh": "الرياض",
"noInstructionsAvailable": "لا توجد طريقة استخدام محددة.",
"contactInformation": "معلومات التواصل:",
+ "contactInfo": "معلومات التواصل",
"humanResources": "الموارد البشرية",
"drSulaimanAlHabib": "سليمان الحبيب",
"allCategories": "الكل",
diff --git a/assets/langs/en-US.json b/assets/langs/en-US.json
index 6e194e9..03277bb 100644
--- a/assets/langs/en-US.json
+++ b/assets/langs/en-US.json
@@ -711,6 +711,7 @@
"riyadh": "Riyadh",
"noInstructionsAvailable": "No instructions available.",
"contactInformation": "Contact Information:",
+ "contactInfo": "Contact Information",
"humanResources": "Human Resources",
"drSulaimanAlHabib": "DR SULAIMAN AL HABIB",
"allCategories": "All",
diff --git a/lib/api/api_client.dart b/lib/api/api_client.dart
index edce588..0ef23ed 100644
--- a/lib/api/api_client.dart
+++ b/lib/api/api_client.dart
@@ -1,5 +1,6 @@
import 'dart:async';
import 'dart:convert';
+import 'dart:developer';
import 'dart:io';
import 'package:flutter/cupertino.dart';
@@ -86,15 +87,14 @@ class ApiClient {
_headers.addAll(headers);
}
if (!kReleaseMode) {
- print("Url:$url");
+ log("Url: $url");
var bodyJson = json.encode(jsonObject);
- print("body:$bodyJson" );
+ log("body:$bodyJson");
}
var response = await postJsonForResponse(url, jsonObject, token: token, queryParameters: queryParameters, headers: _headers, retryTimes: retryTimes, isFormData: isFormData);
try {
if (!kReleaseMode) {
- logger.i("Url: " + url);
- logger.i("res: " + response.body);
+ log("res: " + response.body);
}
var jsonData = jsonDecode(response.body);
@@ -112,7 +112,7 @@ class ApiClient {
} else if (jsonData["MessageStatus"] == 2 && jsonData["IsOTPMaxLimitExceed"] == true) {
await Utils.performLogout(AppRoutes.navigatorKey.currentContext, null);
throw const APIException(APIException.UNAUTHORIZED, error: null);
- } else {
+ } else {
APIError? apiError;
apiError = APIError(jsonData['ErrorCode'], jsonData['ErrorEndUserMessage'], jsonData['ErrorType'] ?? 0, jsonData['ErrorStatusCode']);
throw APIException(APIException.BAD_REQUEST, error: apiError);
diff --git a/lib/classes/consts.dart b/lib/classes/consts.dart
index dfe42c4..e346378 100644
--- a/lib/classes/consts.dart
+++ b/lib/classes/consts.dart
@@ -1,15 +1,15 @@
import 'package:mohem_flutter_app/ui/marathon/widgets/question_card.dart';
class ApiConsts {
- // static String baseUrl = "http://10.201.214.101:2027"; // Local server
+ // static String baseUrl = "http://10.201.214.101:2027"; // Local server
// static String baseUrl = "https://erptstapp.srca.org.sa"; // SRCA server
- // static String baseUrl = "https://uat.hmgwebservices.com"; // UAT ser343622ver
- // static String baseUrl = "http://10.201.204.101:2024";
- // static String baseUrl = "https://webservices.hmg.com"; // PreProd
- // static String baseUrl = "https://hmgwebservices.com"; // Live server
+ static String baseUrl = "https://uat.hmgwebservices.com"; // UAT ser343622ver
+ // static String baseUrl = "http://10.201.204.101:2024";
+ // static String baseUrl = "https://webservices.hmg.com"; // PreProd
+ // static String baseUrl = "https://hmgwebservices.com"; // Live server
- static String baseUrl = "https://mohemm.hmg.com"; // New Live server
+ // static String baseUrl = "https://mohemm.hmg.com"; // New Live server
static String ssoBaseUrl = "https://sso.hmg.com"; // New Live server
//
// static String baseUrl = "https://uat.hmgwebservices.com"; // UAT ser343622ver
@@ -21,8 +21,8 @@ class ApiConsts {
// static String baseUrl = "http://10.20.200.111:1010/";
- // static String baseUrl = "https://webservices.hmg.com"; // PreProd
- // static String baseUrl = "https://mohemm.hmg.com";
+ // static String baseUrl = "https://webservices.hmg.com"; // PreProd
+ // static String baseUrl = "https://mohemm.hmg.com";
// static String baseUrl = "https://hmgwebservices.com"; // Live server
static String baseUrlServices = baseUrl + "/Services/"; // server
@@ -48,7 +48,6 @@ class ApiConsts {
static String ssoAuthRedirection = ssoBaseUrl + "/api/auth/connect";
-
//Chat
static String chatServerBaseUrl = "https://apiderichat.hmg.com/";
static String chatServerBaseApiUrl = chatServerBaseUrl + "api/";
@@ -99,11 +98,15 @@ class ApiConsts {
static String notificationCenterForgotPasswordServiceGUID = "5CCCC2C9-EEA0-4207-9BE9-EE1FBBFFE8D0";
static MemberType selectedMemberType = MemberType.employee;
+
static bool get ISDependetLogin => selectedMemberType == MemberType.dependent;
+
static set ISDependetLogin(bool value) {
selectedMemberType = value ? MemberType.dependent : MemberType.employee;
}
+
static bool get isDependetLogin => ISDependetLogin;
+
static set isDependetLogin(bool value) => ISDependetLogin = value;
}
@@ -112,9 +115,11 @@ enum MemberType {
dependent(2);
final int value;
+
const MemberType(this.value);
bool get isEmployee => this == MemberType.employee;
+
bool get isDependent => this == MemberType.dependent;
static MemberType fromValue(int? value) {
@@ -136,7 +141,3 @@ class SharedPrefsConsts {
static String mohemmWifiPassword = "mohemmWifiPassword";
static String editItemForSale = "editItemForSale";
}
-
-
-
-
diff --git a/lib/extensions/string_extensions.dart b/lib/extensions/string_extensions.dart
index a3dddff..a6f2476 100644
--- a/lib/extensions/string_extensions.dart
+++ b/lib/extensions/string_extensions.dart
@@ -63,63 +63,58 @@ extension EmailValidator on String {
Widget get toWidget => Text(this);
Widget toText10({Color? color, bool isBold = false, int? maxlines, FontStyle? fontStyle}) => Text(
- this,
- maxLines: maxlines,
- style: TextStyle(fontSize: 10, fontStyle: fontStyle ?? FontStyle.normal, fontWeight: isBold ? FontWeight.bold : FontWeight.w600, color: color ?? MyColors.darkTextColor, letterSpacing: -0.4),
- );
+ this,
+ maxLines: maxlines,
+ style: TextStyle(fontSize: 10, fontStyle: fontStyle ?? FontStyle.normal, fontWeight: isBold ? FontWeight.bold : FontWeight.w600, color: color ?? MyColors.darkTextColor, letterSpacing: -0.4),
+ );
+
+ Widget toText11({Color? color, FontWeight? weight, bool isUnderLine = false, bool isBold = false, bool isCenter = false, int maxLine = 0, TextOverflow? overflow}) => Text(
+ this,
+ textAlign: isCenter ? TextAlign.center : null,
+ maxLines: (maxLine > 0) ? maxLine : null,
+ overflow: overflow,
+ style: TextStyle(
+ fontSize: 11,
+ fontWeight: weight ?? (isBold ? FontWeight.bold : FontWeight.w600),
+ color: color ?? MyColors.darkTextColor,
+ letterSpacing: -0.33,
+ decoration: isUnderLine ? TextDecoration.underline : null,
+ ),
+ );
+
+ Widget toText12({Color? color, bool isUnderLine = false, Color? underlineColor, double underlineGap = 2, bool isBold = false, bool isCenter = false, int? maxLine, TextOverflow? overflow}) {
+ Widget text = Text(
+ this,
+ textAlign: isCenter ? TextAlign.center : null,
+ maxLines: maxLine == 0 ? null : maxLine,
+ style: TextStyle(fontSize: 12, fontWeight: isBold ? FontWeight.bold : FontWeight.w600, color: color ?? MyColors.darkTextColor, letterSpacing: -0.72, overflow: TextOverflow.visible),
+ );
- Widget toText11({Color? color, FontWeight? weight, bool isUnderLine = false, bool isBold = false, bool isCenter = false, int maxLine = 0}) => Text(
- this,
- textAlign: isCenter ? TextAlign.center : null,
- maxLines: (maxLine > 0) ? maxLine : null,
- style: TextStyle(
- fontSize: 11,
- fontWeight: weight ?? (isBold ? FontWeight.bold : FontWeight.w600),
- color: color ?? MyColors.darkTextColor,
- letterSpacing: -0.33,
- decoration: isUnderLine ? TextDecoration.underline : null,
- ),
- );
+ if (!isUnderLine) return text;
- Widget toText12({Color? color, bool isUnderLine = false, bool isBold = false, bool isCenter = false, int? maxLine, TextOverflow? overflow}) => Text(
- this,
- textAlign: isCenter ? TextAlign.center : null,
- maxLines: maxLine == 0 ? null : maxLine,
- style: TextStyle(
- fontSize: 12,
- fontWeight: isBold ? FontWeight.bold : FontWeight.w600,
- color: color ?? MyColors.darkTextColor,
- letterSpacing: -0.72,
- decoration: isUnderLine ? TextDecoration.underline : null,
- overflow: TextOverflow.visible
- ),
- );
-
- Widget toText12Auto({Color? color, bool isUnderLine = false, bool isBold = false, bool isCenter = false, int? maxLine,TextOverflow? overflow}) => AutoSizeText(
- this,
- textAlign: isCenter ? TextAlign.center : null,
- maxLines: maxLine == 0 ? null : maxLine,
- minFontSize: 8,
- style: TextStyle(
- fontSize: 12,
- fontWeight: isBold ? FontWeight.bold : FontWeight.w600,
- color: color ?? MyColors.darkTextColor,
- overflow: overflow,
- letterSpacing: -0.72,
- decoration: isUnderLine ? TextDecoration.underline : null,
- ),
- );
+ return Container(
+ padding: EdgeInsets.only(bottom: underlineGap),
+ decoration: BoxDecoration(border: Border(bottom: BorderSide(color: underlineColor ?? color ?? MyColors.darkTextColor, width: 1))),
+ child: text,
+ );
+ }
- Widget toTextAuto({
- Color? color,
- bool isUnderLine = false,
- bool isBold = false,
- bool isCenter = false,
- int maxLine = 0,
- double fontSize = 12,
- double letterSpacing = -0.72,
- double height = 1,
- }) =>
+ Widget toText12Auto({Color? color, bool isUnderLine = false, bool isBold = false, bool isCenter = false, int? maxLine, TextOverflow? overflow}) => AutoSizeText(
+ this,
+ textAlign: isCenter ? TextAlign.center : null,
+ maxLines: maxLine == 0 ? null : maxLine,
+ minFontSize: 8,
+ style: TextStyle(
+ fontSize: 12,
+ fontWeight: isBold ? FontWeight.bold : FontWeight.w600,
+ color: color ?? MyColors.darkTextColor,
+ overflow: overflow,
+ letterSpacing: -0.72,
+ decoration: isUnderLine ? TextDecoration.underline : null,
+ ),
+ );
+
+ Widget toTextAuto({Color? color, bool isUnderLine = false, bool isBold = false, bool isCenter = false, int maxLine = 0, double fontSize = 12, double letterSpacing = -0.72, double height = 1}) =>
AutoSizeText(
this,
textAlign: isCenter ? TextAlign.center : null,
@@ -135,101 +130,102 @@ extension EmailValidator on String {
);
Widget toText13({Color? color, bool isUnderLine = false, bool isBold = false, int? maxlines, TextOverflow? overflow}) => Text(
- this,
- maxLines: maxlines,
- overflow: overflow,
- style: TextStyle(fontSize: 13, fontWeight: isBold ? FontWeight.bold : FontWeight.w600, color: color ?? MyColors.darkTextColor, letterSpacing: -0.52, decoration: isUnderLine ? TextDecoration.underline : null),
- );
-
- Widget toText14({Color? color, bool isUnderLine = false, bool isBold = false, FontWeight? weight, int? maxlines, TextAlign? textAlign, bool isCenter = false}) => Text(
- this,
- textAlign: isCenter ? TextAlign.center : textAlign,
- maxLines: maxlines,
- style: TextStyle(
- color: color ?? MyColors.darkTextColor,
- fontSize: 14,
- letterSpacing: -0.48,
- fontWeight: weight ?? (isBold ? FontWeight.bold : FontWeight.w600),
- decoration: isUnderLine ? TextDecoration.underline : null),
- );
+ this,
+ maxLines: maxlines,
+ overflow: overflow,
+ style: TextStyle(
+ fontSize: 13,
+ fontWeight: isBold ? FontWeight.bold : FontWeight.w600,
+ color: color ?? MyColors.darkTextColor,
+ letterSpacing: -0.52,
+ decoration: isUnderLine ? TextDecoration.underline : null,
+ ),
+ );
+
+ Widget toText14({Color? color, bool isUnderLine = false, bool isBold = false, FontWeight? weight, int? maxlines, TextAlign? textAlign, bool isCenter = false, TextOverflow? overflow}) => Text(
+ this,
+ textAlign: isCenter ? TextAlign.center : textAlign,
+ maxLines: maxlines,
+ style: TextStyle(
+ overflow: overflow,
+ color: color ?? MyColors.darkTextColor,
+ fontSize: 14,
+ letterSpacing: -0.48,
+ fontWeight: weight ?? (isBold ? FontWeight.bold : FontWeight.w600),
+ decoration: isUnderLine ? TextDecoration.underline : null,
+ ),
+ );
Widget toText16({Color? color, bool isUnderLine = false, bool isBold = false, int? maxlines, double? height, bool isCentered = false}) => Text(
- this,
- maxLines: maxlines,
- textAlign: isCentered ? TextAlign.center : null,
- style: TextStyle(
- color: color ?? MyColors.darkTextColor,
- fontSize: 16,
- letterSpacing: -0.64,
- height: height,
- fontWeight: isBold ? FontWeight.bold : FontWeight.w600,
- decoration: isUnderLine ? TextDecoration.underline : null,
- ),
- );
+ this,
+ maxLines: maxlines,
+ textAlign: isCentered ? TextAlign.center : null,
+ style: TextStyle(
+ color: color ?? MyColors.darkTextColor,
+ fontSize: 16,
+ letterSpacing: -0.64,
+ height: height,
+ fontWeight: isBold ? FontWeight.bold : FontWeight.w600,
+ decoration: isUnderLine ? TextDecoration.underline : null,
+ ),
+ );
Widget toText17({Color? color, bool isBold = false, bool isCentered = false}) => Text(
- this,
- textAlign: isCentered ? TextAlign.center : null,
- style: TextStyle(color: color ?? MyColors.darkTextColor, fontSize: 17, letterSpacing: -0.68, fontWeight: isBold ? FontWeight.bold : FontWeight.w600),
- );
+ this,
+ textAlign: isCentered ? TextAlign.center : null,
+ style: TextStyle(color: color ?? MyColors.darkTextColor, fontSize: 17, letterSpacing: -0.68, fontWeight: isBold ? FontWeight.bold : FontWeight.w600),
+ );
Widget toText18({Color? color, bool isBold = false, bool isCentered = false}) => Text(
- this,
- textAlign: isCentered ? TextAlign.center : null,
- style: TextStyle(fontSize: 18, fontWeight: isBold ? FontWeight.bold : FontWeight.w600, color: color ?? MyColors.darkTextColor, letterSpacing: -1.08),
- );
+ this,
+ textAlign: isCentered ? TextAlign.center : null,
+ style: TextStyle(fontSize: 18, fontWeight: isBold ? FontWeight.bold : FontWeight.w600, color: color ?? MyColors.darkTextColor, letterSpacing: -1.08),
+ );
- Widget toText19({Color? color, bool isBold = false}) => Text(
- this,
- style: TextStyle(fontSize: 19, fontWeight: isBold ? FontWeight.bold : FontWeight.w600, color: color ?? MyColors.darkTextColor, letterSpacing: -1.14),
- );
+ Widget toText19({Color? color, bool isBold = false}) =>
+ Text(this, style: TextStyle(fontSize: 19, fontWeight: isBold ? FontWeight.bold : FontWeight.w600, color: color ?? MyColors.darkTextColor, letterSpacing: -1.14));
Widget toText20({Color? color, bool isBold = false, bool isCentered = false}) => Text(
- this,
- textAlign: isCentered ? TextAlign.center : null,
- style: TextStyle(fontSize: 20, fontWeight: isBold ? FontWeight.bold : FontWeight.w600, color: color ?? MyColors.darkTextColor, letterSpacing: -0.4),
- );
+ this,
+ textAlign: isCentered ? TextAlign.center : null,
+ style: TextStyle(fontSize: 20, fontWeight: isBold ? FontWeight.bold : FontWeight.w600, color: color ?? MyColors.darkTextColor, letterSpacing: -0.4),
+ );
- Widget toText21({Color? color, bool isBold = false, FontWeight? weight, int? maxlines}) => Text(
- this,
- maxLines: maxlines,
- style: TextStyle(color: color ?? MyColors.grey3AColor, fontSize: 21, letterSpacing: -0.84, fontWeight: weight ?? (isBold ? FontWeight.bold : FontWeight.w600)),
- );
+ Widget toText21({Color? color, bool isBold = false, FontWeight? weight, int? maxlines}) =>
+ Text(this, maxLines: maxlines, style: TextStyle(color: color ?? MyColors.grey3AColor, fontSize: 21, letterSpacing: -0.84, fontWeight: weight ?? (isBold ? FontWeight.bold : FontWeight.w600)));
Widget toText22({Color? color, bool isBold = false, bool isCentered = false}) => Text(
- this,
- textAlign: isCentered ? TextAlign.center : null,
- style: TextStyle(height: 1, color: color ?? MyColors.darkTextColor, fontSize: 22, letterSpacing: -1.44, fontWeight: isBold ? FontWeight.bold : FontWeight.w600),
- );
+ this,
+ textAlign: isCentered ? TextAlign.center : null,
+ style: TextStyle(height: 1, color: color ?? MyColors.darkTextColor, fontSize: 22, letterSpacing: -1.44, fontWeight: isBold ? FontWeight.bold : FontWeight.w600),
+ );
Widget toText23({Color? color, bool isBold = false, bool isCentered = false}) => Text(
- this,
- textAlign: isCentered ? TextAlign.center : null,
- style: TextStyle(height: 23 / 24, color: color ?? MyColors.darkTextColor, fontSize: 23, letterSpacing: -0.92, fontWeight: isBold ? FontWeight.bold : FontWeight.w600),
- );
+ this,
+ textAlign: isCentered ? TextAlign.center : null,
+ style: TextStyle(height: 23 / 24, color: color ?? MyColors.darkTextColor, fontSize: 23, letterSpacing: -0.92, fontWeight: isBold ? FontWeight.bold : FontWeight.w600),
+ );
Widget toText24({Color? color, bool isBold = false, bool isCentered = false}) => Text(
- this,
- textAlign: isCentered ? TextAlign.center : null,
- style: TextStyle(height: 23 / 24, color: color ?? MyColors.darkTextColor, fontSize: 24, letterSpacing: -1.44, fontWeight: isBold ? FontWeight.bold : FontWeight.w600),
- );
+ this,
+ textAlign: isCentered ? TextAlign.center : null,
+ style: TextStyle(height: 23 / 24, color: color ?? MyColors.darkTextColor, fontSize: 24, letterSpacing: -1.44, fontWeight: isBold ? FontWeight.bold : FontWeight.w600),
+ );
Widget toText30({Color? color, bool isBold = false, bool isCentered = false}) => Text(
- this,
- textAlign: isCentered ? TextAlign.center : null,
- style: TextStyle(height: 20 / 32, color: color ?? MyColors.darkTextColor, fontSize: 32, letterSpacing: -1.2, fontWeight: isBold ? FontWeight.bold : FontWeight.w600),
- );
+ this,
+ textAlign: isCentered ? TextAlign.center : null,
+ style: TextStyle(height: 20 / 32, color: color ?? MyColors.darkTextColor, fontSize: 32, letterSpacing: -1.2, fontWeight: isBold ? FontWeight.bold : FontWeight.w600),
+ );
Widget toText32({Color? color, bool isBold = false, bool isCentered = false}) => Text(
- this,
- textAlign: isCentered ? TextAlign.center : null,
- style: TextStyle(height: 32 / 32, color: color ?? MyColors.darkTextColor, fontSize: 32, letterSpacing: -1.92, fontWeight: isBold ? FontWeight.bold : FontWeight.w600),
- );
+ this,
+ textAlign: isCentered ? TextAlign.center : null,
+ style: TextStyle(height: 32 / 32, color: color ?? MyColors.darkTextColor, fontSize: 32, letterSpacing: -1.92, fontWeight: isBold ? FontWeight.bold : FontWeight.w600),
+ );
- Widget toText44({Color? color, bool isBold = false}) => Text(
- this,
- style: TextStyle(height: 32 / 32, color: color ?? MyColors.darkTextColor, fontSize: 44, letterSpacing: -2.64, fontWeight: isBold ? FontWeight.bold : FontWeight.w600),
- );
+ Widget toText44({Color? color, bool isBold = false}) =>
+ Text(this, style: TextStyle(height: 32 / 32, color: color ?? MyColors.darkTextColor, fontSize: 44, letterSpacing: -2.64, fontWeight: isBold ? FontWeight.bold : FontWeight.w600));
Widget toSectionHeading({String upperHeading = "", String lowerHeading = ""}) {
String upper = "";
@@ -253,14 +249,7 @@ extension EmailValidator on String {
lower = lowerHeading;
}
- return Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- mainAxisSize: MainAxisSize.min,
- children: [
- if (upper.isNotEmpty) upper.toText12(),
- lower.toText24(isBold: true),
- ],
- );
+ return Column(crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [if (upper.isNotEmpty) upper.toText12(), lower.toText24(isBold: true)]);
}
bool isValidEmail() {
diff --git a/lib/generated/codegen_loader.g.dart b/lib/generated/codegen_loader.g.dart
index 242fde5..6fdfdfe 100644
--- a/lib/generated/codegen_loader.g.dart
+++ b/lib/generated/codegen_loader.g.dart
@@ -727,6 +727,7 @@ class CodegenLoader extends AssetLoader{
"riyadh": "الرياض",
"noInstructionsAvailable": "لا توجد طريقة استخدام محددة.",
"contactInformation": "معلومات التواصل:",
+ "contactInfo": "معلومات التواصل",
"humanResources": "الموارد البشرية",
"drSulaimanAlHabib": "سليمان الحبيب",
"allCategories": "الكل",
@@ -1509,6 +1510,7 @@ static const Map _en_US = {
"riyadh": "Riyadh",
"noInstructionsAvailable": "No instructions available.",
"contactInformation": "Contact Information:",
+ "contactInfo": "Contact Information",
"humanResources": "Human Resources",
"drSulaimanAlHabib": "DR SULAIMAN AL HABIB",
"allCategories": "All",
diff --git a/lib/generated/locale_keys.g.dart b/lib/generated/locale_keys.g.dart
index 73e9b86..3fb1e5b 100644
--- a/lib/generated/locale_keys.g.dart
+++ b/lib/generated/locale_keys.g.dart
@@ -699,6 +699,7 @@ abstract class LocaleKeys {
static const riyadh = 'riyadh';
static const noInstructionsAvailable = 'noInstructionsAvailable';
static const contactInformation = 'contactInformation';
+ static const contactInfo = 'contactInfo';
static const humanResources = 'humanResources';
static const drSulaimanAlHabib = 'drSulaimanAlHabib';
static const allCategories = 'allCategories';
diff --git a/lib/theme/app_theme.dart b/lib/theme/app_theme.dart
index f228600..d2d9fab 100644
--- a/lib/theme/app_theme.dart
+++ b/lib/theme/app_theme.dart
@@ -1,3 +1,4 @@
+import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:mohem_flutter_app/classes/colors.dart';
diff --git a/lib/ui/landing/dashboard_screen.dart b/lib/ui/landing/dashboard_screen.dart
index 65165aa..94a49da 100644
--- a/lib/ui/landing/dashboard_screen.dart
+++ b/lib/ui/landing/dashboard_screen.dart
@@ -290,9 +290,7 @@ class _DashboardScreenState extends State with WidgetsBindingOb
});
},
),
- Image
- .asset("assets/images/logos/main_mohemm_logo.png", width: 134, height: 28)
- .expanded,
+ Image.asset("assets/images/logos/main_mohemm_logo.png", width: 134, height: 28).expanded,
SvgPicture.asset("assets/images/announcements.svg", matchTextDirection: true).onPress(() async {
await Navigator.pushNamed(context, AppRoutes.announcements);
}),
@@ -324,103 +322,103 @@ class _DashboardScreenState extends State with WidgetsBindingOb
child: Consumer(
builder: (BuildContext context, DashboardProviderModel model, Widget? child) {
return (model.isAttendanceTrackingLoading
- ? GetAttendanceTrackingShimmer()
- : Container(
- decoration: BoxDecoration(
- borderRadius: BorderRadius.circular(15),
- gradient: const LinearGradient(
- transform: GradientRotation(.46),
- begin: Alignment.topRight,
- end: Alignment.bottomLeft,
- colors: [MyColors.gradiantEndColor, MyColors.gradiantStartColor],
- ),
- ),
- child: Stack(
- alignment: Alignment.center,
- children: [
- if (model.isTimeRemainingInSeconds == 0) SvgPicture.asset("assets/images/thumb.svg"),
- Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- Expanded(
- child: Column(
- mainAxisSize: MainAxisSize.min,
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- LocaleKeys.markAttendance.tr().toText14(color: Colors.white, isBold: true),
- if (model.isTimeRemainingInSeconds == 0) DateTime.now().toString().split(" ")[0].toText12(color: Colors.white),
- if (model.isTimeRemainingInSeconds != 0)
- Column(
- mainAxisSize: MainAxisSize.min,
- crossAxisAlignment: CrossAxisAlignment.start,
+ ? GetAttendanceTrackingShimmer()
+ : Container(
+ decoration: BoxDecoration(
+ borderRadius: BorderRadius.circular(15),
+ gradient: const LinearGradient(
+ transform: GradientRotation(.46),
+ begin: Alignment.topRight,
+ end: Alignment.bottomLeft,
+ colors: [MyColors.gradiantEndColor, MyColors.gradiantStartColor],
+ ),
+ ),
+ child: Stack(
+ alignment: Alignment.center,
+ children: [
+ if (model.isTimeRemainingInSeconds == 0) SvgPicture.asset("assets/images/thumb.svg"),
+ Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Expanded(
+ child: Column(
+ mainAxisSize: MainAxisSize.min,
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ LocaleKeys.markAttendance.tr().toText14(color: Colors.white, isBold: true),
+ if (model.isTimeRemainingInSeconds == 0) DateTime.now().toString().split(" ")[0].toText12(color: Colors.white),
+ if (model.isTimeRemainingInSeconds != 0)
+ Column(
+ mainAxisSize: MainAxisSize.min,
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ 9.height,
+ Directionality(
+ textDirection: ui.TextDirection.ltr,
+ child: CountdownTimer(
+ endTime: model.endTime,
+ onEnd: null,
+ endWidget: "00:00:00".toText14(color: Colors.white, isBold: true),
+ textStyle: const TextStyle(color: Colors.white, fontSize: 14, letterSpacing: -0.48, fontWeight: FontWeight.bold),
+ ),
+ ),
+ LocaleKeys.timeLeftToday.tr().toText12(color: Colors.white),
+ 9.height,
+ ClipRRect(
+ borderRadius: const BorderRadius.all(Radius.circular(20)),
+ child: LinearProgressIndicator(
+ value: model.progress,
+ minHeight: 8,
+ valueColor: const AlwaysStoppedAnimation(Colors.white),
+ backgroundColor: const Color(0xff196D73),
+ ),
+ ),
+ ],
+ ),
+ ],
+ ).paddingOnly(top: 12, right: 15, left: 12),
+ ),
+ Row(
children: [
- 9.height,
- Directionality(
- textDirection: ui.TextDirection.ltr,
- child: CountdownTimer(
- endTime: model.endTime,
- onEnd: null,
- endWidget: "00:00:00".toText14(color: Colors.white, isBold: true),
- textStyle: const TextStyle(color: Colors.white, fontSize: 14, letterSpacing: -0.48, fontWeight: FontWeight.bold),
- ),
+ Expanded(
+ child: Column(
+ mainAxisSize: MainAxisSize.min,
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ LocaleKeys.checkIn.tr().toText12(color: Colors.white),
+ (model.attendanceTracking!.pSwipeIn == null ? "--:--" : model.attendanceTracking!.pSwipeIn).toString().toText14(
+ color: Colors.white,
+ isBold: true,
+ ),
+ 4.height,
+ ],
+ ).paddingOnly(left: 12, right: 12),
),
- LocaleKeys.timeLeftToday.tr().toText12(color: Colors.white),
- 9.height,
- ClipRRect(
- borderRadius: const BorderRadius.all(Radius.circular(20)),
- child: LinearProgressIndicator(
- value: model.progress,
- minHeight: 8,
- valueColor: const AlwaysStoppedAnimation(Colors.white),
- backgroundColor: const Color(0xff196D73),
+ Container(
+ margin: EdgeInsets.only(top: AppState().isArabic(context) ? 6 : 0),
+ width: 45,
+ height: 45,
+ padding: const EdgeInsets.only(left: 10, right: 10),
+ decoration: BoxDecoration(
+ color: const Color(0xff259EA4),
+ borderRadius: BorderRadius.only(
+ bottomRight: AppState().isArabic(context) ? const Radius.circular(0) : const Radius.circular(15),
+ bottomLeft: AppState().isArabic(context) ? const Radius.circular(15) : const Radius.circular(0),
+ ),
),
- ),
+ child: SvgPicture.asset(model.isTimeRemainingInSeconds == 0 ? "assets/images/biometrics.svg" : "assets/images/biometrics.svg"),
+ ).onPress(() {
+ showMyBottomSheet(context, callBackFunc: () {}, child: MarkAttendanceWidget(model, isFromDashboard: true));
+ }),
],
),
- ],
- ).paddingOnly(top: 12, right: 15, left: 12),
- ),
- Row(
- children: [
- Expanded(
- child: Column(
- mainAxisSize: MainAxisSize.min,
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- LocaleKeys.checkIn.tr().toText12(color: Colors.white),
- (model.attendanceTracking!.pSwipeIn == null ? "--:--" : model.attendanceTracking!.pSwipeIn).toString().toText14(
- color: Colors.white,
- isBold: true,
- ),
- 4.height,
- ],
- ).paddingOnly(left: 12, right: 12),
+ ],
),
- Container(
- margin: EdgeInsets.only(top: AppState().isArabic(context) ? 6 : 0),
- width: 45,
- height: 45,
- padding: const EdgeInsets.only(left: 10, right: 10),
- decoration: BoxDecoration(
- color: const Color(0xff259EA4),
- borderRadius: BorderRadius.only(
- bottomRight: AppState().isArabic(context) ? const Radius.circular(0) : const Radius.circular(15),
- bottomLeft: AppState().isArabic(context) ? const Radius.circular(15) : const Radius.circular(0),
- ),
- ),
- child: SvgPicture.asset(model.isTimeRemainingInSeconds == 0 ? "assets/images/biometrics.svg" : "assets/images/biometrics.svg"),
- ).onPress(() {
- showMyBottomSheet(context, callBackFunc: () {}, child: MarkAttendanceWidget(model, isFromDashboard: true));
- }),
],
),
- ],
- ),
- ],
- ),
- ).onPress(() {
- Navigator.pushNamed(context, AppRoutes.todayAttendance);
- }))
+ ).onPress(() {
+ Navigator.pushNamed(context, AppRoutes.todayAttendance);
+ }))
.animatedSwither();
},
),
@@ -455,7 +453,7 @@ class _DashboardScreenState extends State with WidgetsBindingOb
},
).paddingOnly(bottom: 18, left: 24, right: 24),
// eventActivityWidget(context),
- if (isDisplayMazaya) ...[
+ if (isDisplayMazaya) ...[
InkWell(
borderRadius: BorderRadius.circular(20),
onTap: () {
@@ -466,9 +464,7 @@ class _DashboardScreenState extends State with WidgetsBindingOb
child: AspectRatio(
aspectRatio: 386 / 121.37,
child: SvgPicture.asset(
- AppState().isArabic(context)
- ? "assets/images/mazaya_banner_ar.svg"
- : "assets/images/mazaya_banner_en.svg",
+ AppState().isArabic(context) ? "assets/images/mazaya_banner_ar.svg" : "assets/images/mazaya_banner_en.svg",
width: double.infinity,
fit: BoxFit.fill,
),
@@ -570,7 +566,6 @@ class _DashboardScreenState extends State with WidgetsBindingOb
],
),
*/
-
Container(
width: double.infinity,
padding: const EdgeInsets.only(top: 31),
@@ -583,9 +578,7 @@ class _DashboardScreenState extends State with WidgetsBindingOb
mainAxisSize: MainAxisSize.min,
children: [
ServicesWidget(),
- context
- .watch()
- .isLoading ? const MarathonBannerShimmer().paddingAll(20) : const MarathonBanner().paddingOnly(left: 21, right: 21, bottom: 8, top: 8),
+ context.watch().isLoading ? const MarathonBannerShimmer().paddingAll(20) : const MarathonBanner().paddingOnly(left: 21, right: 21, bottom: 8, top: 8),
// context.watch().isTutorialLoading
// ? const MarathonBannerShimmer().paddingAll(20)
// : Container(
@@ -689,28 +682,28 @@ class _DashboardScreenState extends State with WidgetsBindingOb
SvgPicture.asset(
"assets/icons/chat/chat.svg",
color:
- !checkIfPrivilegedForChat()
- ? MyColors.lightGreyE3Color
- : currentIndex == 4
- ? MyColors.grey3AColor
- : cProvider.disbaleChatForThisUser
- ? MyColors.lightGreyE3Color
- : MyColors.grey98Color,
+ !checkIfPrivilegedForChat()
+ ? MyColors.lightGreyE3Color
+ : currentIndex == 4
+ ? MyColors.grey3AColor
+ : cProvider.disbaleChatForThisUser
+ ? MyColors.lightGreyE3Color
+ : MyColors.grey98Color,
).paddingAll(4),
Consumer(
builder: (BuildContext cxt, ChatProviderModel data, Widget? child) {
return !checkIfPrivilegedForChat()
? const SizedBox()
: Positioned(
- right: 0,
- top: 0,
- child: Container(
- padding: const EdgeInsets.only(left: 4, right: 4),
- alignment: Alignment.center,
- decoration: BoxDecoration(color: cProvider.disbaleChatForThisUser ? MyColors.pinkDarkColor : MyColors.redColor, borderRadius: BorderRadius.circular(17)),
- child: data.chatUConvCounter.toString().toText10(color: Colors.white),
- ),
- );
+ right: 0,
+ top: 0,
+ child: Container(
+ padding: const EdgeInsets.only(left: 4, right: 4),
+ alignment: Alignment.center,
+ decoration: BoxDecoration(color: cProvider.disbaleChatForThisUser ? MyColors.pinkDarkColor : MyColors.redColor, borderRadius: BorderRadius.circular(17)),
+ child: data.chatUConvCounter.toString().toText10(color: Colors.white),
+ ),
+ );
},
),
],
@@ -746,16 +739,9 @@ class _DashboardScreenState extends State with WidgetsBindingOb
}
Widget eventActivityWidget(BuildContext context) {
- return (context
- .watch()
- .isEventLoadingLoading)
+ return (context.watch().isEventLoadingLoading)
? const MarathonBannerShimmer().paddingOnly(left: 21, right: 21, bottom: 21, top: 0)
- : (context
- .watch()
- .eventActivity != null && context
- .watch()
- .eventActivity!
- .isActive == true)
+ : (context.watch().eventActivity != null && context.watch().eventActivity!.isActive == true)
? const EventActivityBanner().paddingOnly(left: 21, right: 21, bottom: 21, top: 0)
: const SizedBox();
}
diff --git a/lib/ui/landing/dashboard_screen_for_dependet.dart b/lib/ui/landing/dashboard_screen_for_dependet.dart
index 49fb552..a4ec744 100644
--- a/lib/ui/landing/dashboard_screen_for_dependet.dart
+++ b/lib/ui/landing/dashboard_screen_for_dependet.dart
@@ -14,6 +14,7 @@ import 'package:mohem_flutter_app/extensions/string_extensions.dart';
import 'package:mohem_flutter_app/generated/locale_keys.g.dart';
import 'package:mohem_flutter_app/models/offers_and_discounts/wala_plus_category_model.dart';
import 'package:mohem_flutter_app/models/offers_and_discounts/wala_plus_retailer_model.dart';
+import 'package:mohem_flutter_app/widgets/info_chip_widget.dart';
import 'package:shimmer/shimmer.dart';
class DashboardScreenForDependet extends StatefulWidget {
@@ -105,17 +106,14 @@ class _DashboardScreenForDependetState extends State
String dob = AppState().dependentDob ?? "";
String name = AppState().dependentName ?? "";
String mobile = AppState().postParamsObject?.mobileNumber ?? "";
- String tokenID = (AppState().postParamsObject?.tokenID != null && AppState().postParamsObject!.tokenID!.isNotEmpty)
- ? AppState().postParamsObject!.tokenID!
- : (AppState().authenticationTokenID ?? "");
+ String tokenID =
+ (AppState().postParamsObject?.tokenID != null && AppState().postParamsObject!.tokenID!.isNotEmpty) ? AppState().postParamsObject!.tokenID! : (AppState().authenticationTokenID ?? "");
if (tokenID.isEmpty) {
tokenID = await Utils.getStringFromPrefs("P_DEP_TOKEN_ID");
}
int sessionId = AppState().postParamsObject?.pSessionId ?? 0;
String email = AppState().postParamsObject?.pEmailAddress ?? "";
- String deviceToken = AppState().getIsHuawei
- ? AppState().getHuaweiPushToken
- : (AppState().getDeviceToken ?? "");
+ String deviceToken = AppState().getIsHuawei ? AppState().getHuaweiPushToken : (AppState().getDeviceToken ?? "");
await LoginApiClient().insertMobileLoginInfoDependent(
loginType: loginType,
@@ -178,10 +176,7 @@ class _DashboardScreenForDependetState extends State
List fullList = [];
bool hasAll = list.any((c) => (c.id == "all" || c.name?.toLowerCase() == "all" || c.name == "الجميع" || c.name == "الكل"));
if (!hasAll) {
- fullList.add(WalaPlusCategoryModel(
- id: "all",
- name: LocaleKeys.allCategories.tr(),
- ));
+ fullList.add(WalaPlusCategoryModel(id: "all", name: LocaleKeys.allCategories.tr()));
}
fullList.addAll(list);
@@ -201,10 +196,7 @@ class _DashboardScreenForDependetState extends State
List fullList = [];
bool hasAll = retryList.any((c) => (c.id == "all" || c.name?.toLowerCase() == "all" || c.name == "الجميع" || c.name == "الكل"));
if (!hasAll) {
- fullList.add(WalaPlusCategoryModel(
- id: "all",
- name: LocaleKeys.allCategories.tr(),
- ));
+ fullList.add(WalaPlusCategoryModel(id: "all", name: LocaleKeys.allCategories.tr()));
}
fullList.addAll(retryList);
setState(() {
@@ -221,9 +213,7 @@ class _DashboardScreenForDependetState extends State
if (mounted && categoriesList.isEmpty) {
setState(() {
- categoriesList = [
- WalaPlusCategoryModel(id: "all", name: LocaleKeys.allCategories.tr()),
- ];
+ categoriesList = [WalaPlusCategoryModel(id: "all", name: LocaleKeys.allCategories.tr())];
if (currentCategoryId.isEmpty) {
currentCategoryId = "all";
}
@@ -322,10 +312,7 @@ class _DashboardScreenForDependetState extends State
}
Future _onRefresh() async {
- await Future.wait([
- fetchCategories(),
- fetchOffers(categoryId: currentCategoryId.isEmpty ? "all" : currentCategoryId),
- ]);
+ await Future.wait([fetchCategories(), fetchOffers(categoryId: currentCategoryId.isEmpty ? "all" : currentCategoryId)]);
}
void _runFilter([String? query]) {
@@ -335,40 +322,36 @@ class _DashboardScreenForDependetState extends State
List temp = retailersList;
if (selectedTypeFilter != "all" && selectedTypeFilter.isNotEmpty) {
- temp = temp.where((WalaPlusRetailerModel item) {
- String? norm = item.normalizedType;
- if (selectedTypeFilter == "online") {
- return norm == "online";
- } else if (selectedTypeFilter == "instore") {
- return norm == "instore";
- } else {
- return norm == selectedTypeFilter || (item.type?.trim().toLowerCase() == selectedTypeFilter);
- }
- }).toList();
+ temp =
+ temp.where((WalaPlusRetailerModel item) {
+ String? norm = item.normalizedType;
+ if (selectedTypeFilter == "online") {
+ return norm == "online";
+ } else if (selectedTypeFilter == "instore") {
+ return norm == "instore";
+ } else {
+ return norm == selectedTypeFilter || (item.type?.trim().toLowerCase() == selectedTypeFilter);
+ }
+ }).toList();
}
if (searchQuery.trim().isNotEmpty) {
String q = searchQuery.trim().toLowerCase();
- temp = temp.where((WalaPlusRetailerModel item) {
- String name = (item.name ?? "").toLowerCase();
- String title = (item.offerTitle ?? "").toLowerCase();
- String desc = (item.description ?? "").toLowerCase();
- String city = item.displayCity.toLowerCase();
- return name.contains(q) || title.contains(q) || desc.contains(q) || city.contains(q);
- }).toList();
+ temp =
+ temp.where((WalaPlusRetailerModel item) {
+ String name = (item.name ?? "").toLowerCase();
+ String title = (item.offerTitle ?? "").toLowerCase();
+ String desc = (item.description ?? "").toLowerCase();
+ String city = item.displayCity.toLowerCase();
+ return name.contains(q) || title.contains(q) || desc.contains(q) || city.contains(q);
+ }).toList();
}
_filteredRetailersList = temp;
setState(() {});
}
-
-
void _openRetailer(WalaPlusRetailerModel item) {
- Navigator.pushNamed(
- context,
- AppRoutes.walaPlusRetailerDetails,
- arguments: item,
- );
+ Navigator.pushNamed(context, AppRoutes.walaPlusRetailerDetails, arguments: item);
}
void _performLogout() {
@@ -414,19 +397,11 @@ class _DashboardScreenForDependetState extends State
},
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 8),
- child: Icon(
- isAr ? Icons.arrow_forward_ios_rounded : Icons.arrow_back_ios_new_rounded,
- size: 20,
- color: const Color(0xFF1E293B),
- ),
+ child: Icon(isAr ? Icons.arrow_forward_ios_rounded : Icons.arrow_back_ios_new_rounded, size: 20, color: const Color(0xFF1E293B)),
),
),
const SizedBox(width: 8),
- SvgPicture.asset(
- "assets/icons/mazaya_brand.svg",
- height: 48,
- fit: BoxFit.contain,
- ),
+ SvgPicture.asset("assets/icons/mazaya_brand.svg", height: 48, fit: BoxFit.contain),
],
),
Row(
@@ -439,20 +414,13 @@ class _DashboardScreenForDependetState extends State
width: 24,
height: 24,
color: const Color(0xFF1E293B),
- errorBuilder: (context, error, stackTrace) => SvgPicture.asset(
- 'assets/icons/ic_dependents_top.svg',
- width: 24,
- height: 24,
- colorFilter: const ColorFilter.mode(Color(0xFF1E293B), BlendMode.srcIn),
- ),
+ errorBuilder:
+ (context, error, stackTrace) =>
+ SvgPicture.asset('assets/icons/ic_dependents_top.svg', width: 24, height: 24, colorFilter: const ColorFilter.mode(Color(0xFF1E293B), BlendMode.srcIn)),
),
tooltip: LocaleKeys.addDependents.tr(),
onPressed: () {
- Navigator.pushNamed(
- context,
- AppRoutes.familyMembers,
- arguments: {'fromMazaya': true},
- );
+ Navigator.pushNamed(context, AppRoutes.familyMembers, arguments: {'fromMazaya': true});
},
),
],
@@ -490,10 +458,7 @@ class _DashboardScreenForDependetState extends State
_buildBrowseCategoriesHeader(isAr),
// Categories Section
- if (categoriesList.isNotEmpty)
- _buildCategoriesList(isAr)
- else
- _buildCategoriesShimmer(),
+ if (categoriesList.isNotEmpty) _buildCategoriesList(isAr) else _buildCategoriesShimmer(),
// Type Filter Chips (All, In Store, Online)
_buildTypeFilterChips(isAr),
@@ -507,12 +472,7 @@ class _DashboardScreenForDependetState extends State
GridView.builder(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
- gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
- crossAxisCount: 2,
- childAspectRatio: 0.72,
- crossAxisSpacing: 12,
- mainAxisSpacing: 16,
- ),
+ gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 2, childAspectRatio: 0.85, crossAxisSpacing: 12, mainAxisSpacing: 16),
itemCount: _filteredRetailersList.length,
itemBuilder: (context, index) {
return _buildOfferCard(_filteredRetailersList[index], isAr);
@@ -520,26 +480,10 @@ class _DashboardScreenForDependetState extends State
),
if (isLoadingMore) ...[
16.height,
- Center(
- child: Padding(
- padding: const EdgeInsets.symmetric(vertical: 12),
- child: Image.asset(
- "assets/images/logos/loading_mohemm_logo.gif",
- height: 48.0,
- width: 48.0,
- ),
- ),
- ),
+ Center(child: Padding(padding: const EdgeInsets.symmetric(vertical: 12), child: Image.asset("assets/images/logos/loading_mohemm_logo.gif", height: 48.0, width: 48.0))),
],
] else
- Padding(
- padding: const EdgeInsets.only(top: 40),
- child: Center(
- child: LocaleKeys.noOffersAvailable.tr().toText14(
- color: const Color(0xFF64748B),
- ),
- ),
- ),
+ Padding(padding: const EdgeInsets.only(top: 40), child: Center(child: LocaleKeys.noOffersAvailable.tr().toText14(color: const Color(0xFF64748B)))),
],
),
),
@@ -557,23 +501,13 @@ class _DashboardScreenForDependetState extends State
color: const Color(0xFFFFFFFF),
borderRadius: BorderRadius.circular(16),
border: Border.all(color: const Color(0xFFE8E8E8), width: 1.0),
- boxShadow: [
- BoxShadow(
- color: const Color(0xFF000000).withOpacity(0.0314),
- offset: const Offset(0, 2),
- blurRadius: 5,
- spreadRadius: 0,
- ),
- ],
+ boxShadow: [BoxShadow(color: const Color(0xFF000000).withOpacity(0.0314), offset: const Offset(0, 2), blurRadius: 5, spreadRadius: 0)],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: [
- LocaleKeys.search.tr().toText12(
- isBold: true,
- color: const Color(0xFF2A333C),
- ),
+ LocaleKeys.search.tr().toText12(isBold: true, color: const Color(0xFF2A333C)),
const SizedBox(height: 2),
TextField(
controller: _searchController,
@@ -582,19 +516,10 @@ class _DashboardScreenForDependetState extends State
isDense: true,
contentPadding: EdgeInsets.zero,
hintText: LocaleKeys.searchItems.tr(),
- hintStyle: const TextStyle(
- color: Color(0xFFACAFB1),
- fontSize: 14,
- fontWeight: FontWeight.w400,
- height: 1.2,
- ),
+ hintStyle: const TextStyle(color: Color(0xFFACAFB1), fontSize: 14, fontWeight: FontWeight.w400, height: 1.2),
border: InputBorder.none,
),
- style: const TextStyle(
- color: Color(0xFF2A333C),
- fontSize: 14,
- fontWeight: FontWeight.w400,
- ),
+ style: const TextStyle(color: Color(0xFF2A333C), fontSize: 14, fontWeight: FontWeight.w400),
),
],
),
@@ -602,13 +527,7 @@ class _DashboardScreenForDependetState extends State
}
Widget _buildBrowseCategoriesHeader(bool isAr) {
- return Padding(
- padding: const EdgeInsets.only(top: 22, bottom: 14),
- child: LocaleKeys.browseCategories.tr().toText18(
- isBold: true,
- color: const Color(0xFF1E293B),
- ),
- );
+ return Padding(padding: const EdgeInsets.only(top: 22, bottom: 14), child: LocaleKeys.browseCategories.tr().toText18(isBold: true, color: const Color(0xFF1E293B)));
}
Widget _buildCategoriesList(bool isAr) {
@@ -623,8 +542,7 @@ class _DashboardScreenForDependetState extends State
separatorBuilder: (_, __) => const SizedBox(width: 14),
itemBuilder: (BuildContext context, int index) {
WalaPlusCategoryModel item = categoriesList[index];
- bool isSelected = (item.id == "all" && (currentCategoryId == "all" || currentCategoryId.isEmpty)) ||
- (currentCategoryId.isNotEmpty && currentCategoryId == item.id);
+ bool isSelected = (item.id == "all" && (currentCategoryId == "all" || currentCategoryId.isEmpty)) || (currentCategoryId.isNotEmpty && currentCategoryId == item.id);
String title = (item.id == "all") ? LocaleKeys.allCategories.tr() : (item.name ?? "");
return InkWell(
@@ -691,22 +609,13 @@ class _DashboardScreenForDependetState extends State
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
alignment: Alignment.center,
- decoration: BoxDecoration(
- borderRadius: BorderRadius.circular(6),
- color: isSelected ? MyColors.darkIconColor : MyColors.lightGreyEAColor,
- ),
+ decoration: BoxDecoration(borderRadius: BorderRadius.circular(6), color: isSelected ? MyColors.darkIconColor : MyColors.lightGreyEAColor),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
- Icon(
- icon,
- size: 14,
- color: isSelected ? MyColors.white : MyColors.black,
- ),
+ Icon(icon, size: 14, color: isSelected ? MyColors.white : MyColors.black),
const SizedBox(width: 5),
- title.toText12(
- color: isSelected ? MyColors.white : MyColors.black,
- ),
+ title.toText12(color: isSelected ? MyColors.white : MyColors.black),
],
),
),
@@ -723,26 +632,14 @@ class _DashboardScreenForDependetState extends State
const double borderWidthVal = 2.8;
const Color activeBorderColor = Color(0xFF00A86B);
- bool isAll = (item.id == "all" ||
- item.id == "0" ||
- (item.name ?? "").toLowerCase() == "all" ||
- item.name == "الجميع" ||
- item.name == "الكل");
+ bool isAll = (item.id == "all" || item.id == "0" || (item.name ?? "").toLowerCase() == "all" || item.name == "الجميع" || item.name == "الكل");
Widget imageContent;
if (isAll) {
- imageContent = Image.asset(
- 'assets/images/cat_all.png',
- width: double.infinity,
- height: double.infinity,
- fit: BoxFit.cover,
- errorBuilder: (_, __, ___) => _getFallbackCategoryAsset(item),
- );
+ imageContent = Image.asset('assets/images/cat_all.png', width: double.infinity, height: double.infinity, fit: BoxFit.cover, errorBuilder: (_, __, ___) => _getFallbackCategoryAsset(item));
} else {
- String categoryImageUrl = item.categoryImageUrl.isNotEmpty
- ? item.categoryImageUrl
- : (item.image?.trim() ?? "");
+ String categoryImageUrl = item.categoryImageUrl.isNotEmpty ? item.categoryImageUrl : (item.image?.trim() ?? "");
if (categoryImageUrl.isNotEmpty) {
if (categoryImageUrl.toLowerCase().endsWith('.svg') || item.isSvg) {
@@ -751,22 +648,10 @@ class _DashboardScreenForDependetState extends State
width: double.infinity,
height: double.infinity,
fit: BoxFit.cover,
- placeholderBuilder: (_) => const Center(
- child: SizedBox(
- width: 20,
- height: 20,
- child: CircularProgressIndicator(strokeWidth: 2, color: Color(0xFF00A86B)),
- ),
- ),
+ placeholderBuilder: (_) => const Center(child: SizedBox(width: 20, height: 20, child: CircularProgressIndicator(strokeWidth: 2, color: Color(0xFF00A86B)))),
);
} else {
- imageContent = Image.network(
- categoryImageUrl,
- width: double.infinity,
- height: double.infinity,
- fit: BoxFit.cover,
- errorBuilder: (_, __, ___) => _getFallbackCategoryAsset(item),
- );
+ imageContent = Image.network(categoryImageUrl, width: double.infinity, height: double.infinity, fit: BoxFit.cover, errorBuilder: (_, __, ___) => _getFallbackCategoryAsset(item));
}
} else {
imageContent = _getFallbackCategoryAsset(item);
@@ -779,19 +664,9 @@ class _DashboardScreenForDependetState extends State
decoration: BoxDecoration(
color: Colors.transparent,
borderRadius: BorderRadius.circular(borderRadiusVal),
- border: isSelected
- ? Border.all(
- color: activeBorderColor,
- width: borderWidthVal,
- )
- : null,
- ),
- child: ClipRRect(
- borderRadius: BorderRadius.circular(
- isSelected ? (borderRadiusVal - borderWidthVal) : borderRadiusVal,
- ),
- child: imageContent,
+ border: isSelected ? Border.all(color: activeBorderColor, width: borderWidthVal) : null,
),
+ child: ClipRRect(borderRadius: BorderRadius.circular(isSelected ? (borderRadiusVal - borderWidthVal) : borderRadiusVal), child: imageContent),
);
}
@@ -799,59 +674,20 @@ class _DashboardScreenForDependetState extends State
String name = (item.name ?? "").toLowerCase();
if (item.id == "all" || item.id == "0" || name == "all" || name == "الكل" || name == "الجميع") {
- return Image.asset(
- 'assets/images/cat_all.png',
- width: double.infinity,
- height: double.infinity,
- fit: BoxFit.cover,
- errorBuilder: (_, __, ___) => _buildFallbackCategoryIconWidget(item),
- );
+ return Image.asset('assets/images/cat_all.png', width: double.infinity, height: double.infinity, fit: BoxFit.cover, errorBuilder: (_, __, ___) => _buildFallbackCategoryIconWidget(item));
} else if (name.contains("shop") || name.contains("retail") || name.contains("تسوق") || name.contains("تجزئة")) {
- return Image.asset(
- 'assets/images/cat_retail.png',
- width: double.infinity,
- height: double.infinity,
- fit: BoxFit.cover,
- errorBuilder: (_, __, ___) => _buildFallbackCategoryIconWidget(item),
- );
- } else if (name.contains("food") ||
- name.contains("restaurant") ||
- name.contains("مطاعم") ||
- name.contains("أغذية") ||
- name.contains("cafe") ||
- name.contains("مقهى")) {
- return Image.asset(
- 'assets/images/cat_food.png',
- width: double.infinity,
- height: double.infinity,
- fit: BoxFit.cover,
- errorBuilder: (_, __, ___) => _buildFallbackCategoryIconWidget(item),
- );
- } else if (name.contains("fitness") ||
- name.contains("wellness") ||
- name.contains("sport") ||
- name.contains("gym") ||
- name.contains("رياضة") ||
- name.contains("لياقة") ||
- name.contains("صحة")) {
- return Image.asset(
- 'assets/images/cat_fitness.png',
- width: double.infinity,
- height: double.infinity,
- fit: BoxFit.cover,
- errorBuilder: (_, __, ___) => _buildFallbackCategoryIconWidget(item),
- );
+ return Image.asset('assets/images/cat_retail.png', width: double.infinity, height: double.infinity, fit: BoxFit.cover, errorBuilder: (_, __, ___) => _buildFallbackCategoryIconWidget(item));
+ } else if (name.contains("food") || name.contains("restaurant") || name.contains("مطاعم") || name.contains("أغذية") || name.contains("cafe") || name.contains("مقهى")) {
+ return Image.asset('assets/images/cat_food.png', width: double.infinity, height: double.infinity, fit: BoxFit.cover, errorBuilder: (_, __, ___) => _buildFallbackCategoryIconWidget(item));
+ } else if (name.contains("fitness") || name.contains("wellness") || name.contains("sport") || name.contains("gym") || name.contains("رياضة") || name.contains("لياقة") || name.contains("صحة")) {
+ return Image.asset('assets/images/cat_fitness.png', width: double.infinity, height: double.infinity, fit: BoxFit.cover, errorBuilder: (_, __, ___) => _buildFallbackCategoryIconWidget(item));
}
return _buildFallbackCategoryIconWidget(item);
}
Widget _buildFallbackCategoryIconWidget(WalaPlusCategoryModel item) {
- return Container(
- color: Colors.transparent,
- alignment: Alignment.center,
- child: _getFallbackCategoryIcon(item, const Color(0xFF168A89)),
- );
+ return Container(color: Colors.transparent, alignment: Alignment.center, child: _getFallbackCategoryIcon(item, const Color(0xFF168A89)));
}
Widget _getFallbackCategoryIcon(WalaPlusCategoryModel item, Color iconColor) {
@@ -885,11 +721,7 @@ class _DashboardScreenForDependetState extends State
iconData = Icons.home_rounded;
}
- return Icon(
- iconData,
- color: color,
- size: 26,
- );
+ return Icon(iconData, color: color, size: 26);
}
String _getDiscountBadge(WalaPlusRetailerModel item) {
@@ -911,23 +743,12 @@ class _DashboardScreenForDependetState extends State
}
Widget _buildMerchantMIcon() {
- return Container(
- color: const Color(0xFFED1C24),
- alignment: Alignment.center,
- child: "M".toText10(
- isBold: true,
- color: Colors.white,
- ),
- );
+ return Container(color: const Color(0xFFED1C24), alignment: Alignment.center, child: "M".toText10(isBold: true, color: Colors.white));
}
Widget _buildOfferCard(WalaPlusRetailerModel item, bool isAr) {
- String? bannerUrl = (item.imageThumb != null && item.imageThumb!.isNotEmpty)
- ? item.imageThumb
- : item.image;
- String? logoUrl = (item.logoThumb != null && item.logoThumb!.isNotEmpty)
- ? item.logoThumb
- : item.logo;
+ String? bannerUrl = (item.imageThumb != null && item.imageThumb!.isNotEmpty) ? item.imageThumb : item.image;
+ String? logoUrl = (item.logoThumb != null && item.logoThumb!.isNotEmpty) ? item.logoThumb : item.logo;
String discountText = _getDiscountBadge(item);
@@ -947,9 +768,7 @@ class _DashboardScreenForDependetState extends State
String validityText = item.getDisplayValidity();
String usageText = "";
if (item.displayUsage.isNotEmpty) {
- usageText = item.displayUsage
- .replaceAll('usage:', LocaleKeys.usageLabel.tr())
- .replaceAll('Usage :', LocaleKeys.usageLabel.tr());
+ usageText = item.displayUsage.replaceAll('usage:', LocaleKeys.usageLabel.tr()).replaceAll('Usage :', LocaleKeys.usageLabel.tr());
}
String cityText = item.getDisplayCity(isAr: isAr);
if (cityText.trim().isEmpty) {
@@ -963,14 +782,7 @@ class _DashboardScreenForDependetState extends State
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(20),
- boxShadow: [
- BoxShadow(
- color: const Color(0xFF0D1720).withOpacity(0.0392),
- offset: const Offset(0, 5),
- blurRadius: 18,
- spreadRadius: 0,
- ),
- ],
+ boxShadow: [BoxShadow(color: const Color(0xFF0D1720).withOpacity(0.0392), offset: const Offset(0, 5), blurRadius: 18, spreadRadius: 0)],
),
padding: const EdgeInsets.all(10),
child: Column(
@@ -980,7 +792,7 @@ class _DashboardScreenForDependetState extends State
ClipRRect(
borderRadius: BorderRadius.circular(14),
child: SizedBox(
- height: 105,
+ height: 120,
width: double.infinity,
child: Stack(
fit: StackFit.expand,
@@ -988,17 +800,11 @@ class _DashboardScreenForDependetState extends State
// Banner Image
(bannerUrl != null && bannerUrl.isNotEmpty)
? Image.network(
- bannerUrl,
- fit: BoxFit.cover,
- errorBuilder: (_, __, ___) => Container(
- color: const Color(0xFFF1F5F9),
- child: const Icon(Icons.image_outlined, color: Color(0xFF94A3B8), size: 32),
- ),
- )
- : Container(
- color: const Color(0xFFF1F5F9),
- child: const Icon(Icons.image_outlined, color: Color(0xFF94A3B8), size: 32),
- ),
+ bannerUrl,
+ fit: BoxFit.cover,
+ errorBuilder: (_, __, ___) => Container(color: const Color(0xFFF1F5F9), child: const Icon(Icons.image_outlined, color: Color(0xFF94A3B8), size: 32)),
+ )
+ : Container(color: const Color(0xFFF1F5F9), child: const Icon(Icons.image_outlined, color: Color(0xFF94A3B8), size: 32)),
// Gradient overlay at bottom of image
Positioned(
@@ -1007,42 +813,19 @@ class _DashboardScreenForDependetState extends State
right: 0,
height: 50,
child: Container(
- decoration: BoxDecoration(
- gradient: LinearGradient(
- begin: Alignment.bottomCenter,
- end: Alignment.topCenter,
- colors: [
- Colors.black.withOpacity(0.75),
- Colors.transparent,
- ],
- ),
- ),
+ decoration: BoxDecoration(gradient: LinearGradient(begin: Alignment.bottomCenter, end: Alignment.topCenter, colors: [Colors.black.withOpacity(0.75), Colors.transparent])),
),
),
// Discount badge at top right (flush with edge, pill on start side)
if (discountText.isNotEmpty)
PositionedDirectional(
- top: 8,
+ top: 20,
end: 0,
child: Container(
- padding: const EdgeInsetsDirectional.only(
- start: 14,
- end: 10,
- top: 4,
- bottom: 4,
- ),
- decoration: const BoxDecoration(
- color: Color(0xFFED1C24),
- borderRadius: BorderRadiusDirectional.only(
- topStart: Radius.circular(20),
- bottomStart: Radius.circular(20),
- ),
- ),
- child: discountText.toText14(
- isBold: true,
- color: Colors.white,
- ),
+ padding: const EdgeInsetsDirectional.only(start: 14, end: 10, top: 4, bottom: 4),
+ decoration: const BoxDecoration(color: Color(0xFFED1C24), borderRadius: BorderRadiusDirectional.only(topStart: Radius.circular(20), bottomStart: Radius.circular(20))),
+ child: discountText.toText14(isBold: true, color: Colors.white),
),
),
@@ -1056,27 +839,12 @@ class _DashboardScreenForDependetState extends State
Container(
width: 18,
height: 18,
- decoration: BoxDecoration(
- color: Colors.white,
- borderRadius: BorderRadius.circular(4),
- ),
+ decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(4)),
clipBehavior: Clip.antiAlias,
- child: (logoUrl != null && logoUrl.isNotEmpty)
- ? Image.network(
- logoUrl,
- fit: BoxFit.contain,
- errorBuilder: (_, __, ___) => _buildMerchantMIcon(),
- )
- : _buildMerchantMIcon(),
+ child: (logoUrl != null && logoUrl.isNotEmpty) ? Image.network(logoUrl, fit: BoxFit.contain, errorBuilder: (_, __, ___) => _buildMerchantMIcon()) : _buildMerchantMIcon(),
),
const SizedBox(width: 5),
- Expanded(
- child: (item.name ?? "").toText11(
- isBold: true,
- maxLine: 1,
- color: Colors.white,
- ),
- ),
+ Expanded(child: (item.name ?? "").toText11(isBold: true, maxLine: 1, color: Colors.white)),
],
),
),
@@ -1088,25 +856,11 @@ class _DashboardScreenForDependetState extends State
const SizedBox(height: 8),
// Retailer Name / Bundle Title
- (item.name ?? "").toText13(
- isBold: true,
- maxlines: 1,
- overflow: TextOverflow.ellipsis,
- color: const Color(0xFF1E293B),
- ),
- if (offerSubtitle.isNotEmpty) ...[
- const SizedBox(height: 2),
- offerSubtitle.toText11(
- maxLine: 1,
- color: const Color(0xFF64748B),
- ),
- ],
+ (item.name ?? "").toText14(isBold: true, maxlines: 1, overflow: TextOverflow.ellipsis, color: const Color(0xFF1E293B)),
+ if (offerSubtitle.isNotEmpty) ...[const SizedBox(height: 2), offerSubtitle.toText14(maxlines: 1, overflow: TextOverflow.ellipsis, isBold: true, color: const Color(0xFF1E293B))],
// Badges Row: Type, Validity, Location & Usage
- if ((typeText != null && typeText.trim().isNotEmpty) ||
- validityText.trim().isNotEmpty ||
- cityText.trim().isNotEmpty ||
- usageText.trim().isNotEmpty) ...[
+ if ((typeText != null && typeText.trim().isNotEmpty) || validityText.trim().isNotEmpty || cityText.trim().isNotEmpty || usageText.trim().isNotEmpty) ...[
const SizedBox(height: 6),
Wrap(
spacing: 5,
@@ -1114,80 +868,30 @@ class _DashboardScreenForDependetState extends State
children: [
// Type Chip (Online or In Store)
if (typeText != null && typeText.trim().isNotEmpty)
- Container(
- padding: const EdgeInsets.symmetric(horizontal: 7, vertical: 2.5),
- decoration: BoxDecoration(
- color: isOnline ? const Color(0xFFEFF6FF) : const Color(0xFFF5F3FF),
- borderRadius: BorderRadius.circular(10),
- ),
- child: Row(
- mainAxisSize: MainAxisSize.min,
- children: [
- Icon(
- isOnline ? Icons.language_rounded : Icons.storefront_rounded,
- size: 10,
- color: isOnline ? const Color(0xFF2563EB) : const Color(0xFF7C3AED),
- ),
- const SizedBox(width: 3),
- typeText.toText10(
- isBold: true,
- color: isOnline ? const Color(0xFF2563EB) : const Color(0xFF7C3AED),
- ),
- ],
- ),
+ InfoChipWidget(
+ text: typeText,
+ icon: isOnline ? Icons.language_rounded : Icons.storefront_rounded,
+ backgroundColor: isOnline ? const Color(0xFFEFF6FF) : const Color(0xFFF5F3FF),
+ textColor: isOnline ? const Color(0xFF2563EB) : const Color(0xFF7C3AED),
),
// Validity badge
- if (validityText.trim().isNotEmpty)
- Container(
- padding: const EdgeInsets.symmetric(horizontal: 7, vertical: 2.5),
- decoration: BoxDecoration(
- color: const Color(0xFFE6F7F0),
- borderRadius: BorderRadius.circular(10),
- ),
- child: validityText.toText10(
- isBold: true,
- color: const Color(0xFF00A651),
- ),
- ),
+ if (validityText.trim().isNotEmpty) InfoChipWidget(text: validityText, backgroundColor: const Color(0xFFE6F7F0), textColor: const Color(0xFF00A651)),
// Location Badge
if (cityText.trim().isNotEmpty)
- Container(
- padding: const EdgeInsets.symmetric(horizontal: 7, vertical: 2.5),
- decoration: BoxDecoration(
- color: const Color(0xFFF3F4F6),
- borderRadius: BorderRadius.circular(10),
- ),
- child: Row(
- mainAxisSize: MainAxisSize.min,
- children: [
- const Icon(
- Icons.location_on_rounded,
- size: 10,
- color: Color(0xFF64748B),
- ),
- const SizedBox(width: 2.5),
- cityText.toText10(
- color: const Color(0xFF4B5563),
- ),
- ],
- ),
+ InfoChipWidget(
+ text: cityText,
+ svgIcon: "assets/icons/location.svg",
+ isBold: false,
+ backgroundColor: const Color(0xFFF3F4F6),
+ textColor: const Color(0xFF4B5563),
+ iconColor: const Color(0xFF64748B),
+ iconTextGap: 2.5,
),
// Usage badge
- if (usageText.trim().isNotEmpty)
- Container(
- padding: const EdgeInsets.symmetric(horizontal: 7, vertical: 2.5),
- decoration: BoxDecoration(
- color: const Color(0xFFFEF3C7),
- borderRadius: BorderRadius.circular(10),
- ),
- child: usageText.toText10(
- isBold: true,
- color: const Color(0xFFB45309),
- ),
- ),
+ if (usageText.trim().isNotEmpty) InfoChipWidget(text: usageText, backgroundColor: const Color(0xFFFEF3C7), textColor: const Color(0xFFB45309)),
],
),
],
@@ -1204,78 +908,28 @@ class _DashboardScreenForDependetState extends State
child: GridView.builder(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
- gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
- crossAxisCount: 2,
- childAspectRatio: 0.72,
- crossAxisSpacing: 12,
- mainAxisSpacing: 16,
- ),
+ gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 2, childAspectRatio: 0.72, crossAxisSpacing: 12, mainAxisSpacing: 16),
itemCount: 4,
itemBuilder: (_, __) {
return Container(
- decoration: BoxDecoration(
- color: Colors.white,
- borderRadius: BorderRadius.circular(20),
- ),
+ decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(20)),
padding: const EdgeInsets.all(10),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
- Container(
- height: 105,
- width: double.infinity,
- decoration: BoxDecoration(
- color: Colors.white,
- borderRadius: BorderRadius.circular(14),
- ),
- ),
+ Container(height: 105, width: double.infinity, decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(14))),
const SizedBox(height: 8),
- Container(
- height: 14,
- width: 110,
- decoration: BoxDecoration(
- color: Colors.white,
- borderRadius: BorderRadius.circular(4),
- ),
- ),
+ Container(height: 14, width: 110, decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(4))),
const SizedBox(height: 4),
- Container(
- height: 12,
- width: 80,
- decoration: BoxDecoration(
- color: Colors.white,
- borderRadius: BorderRadius.circular(4),
- ),
- ),
+ Container(height: 12, width: 80, decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(4))),
const SizedBox(height: 6),
Wrap(
spacing: 5,
runSpacing: 4,
children: [
- Container(
- height: 16,
- width: 50,
- decoration: BoxDecoration(
- color: Colors.white,
- borderRadius: BorderRadius.circular(10),
- ),
- ),
- Container(
- height: 16,
- width: 45,
- decoration: BoxDecoration(
- color: Colors.white,
- borderRadius: BorderRadius.circular(10),
- ),
- ),
- Container(
- height: 16,
- width: 45,
- decoration: BoxDecoration(
- color: Colors.white,
- borderRadius: BorderRadius.circular(10),
- ),
- ),
+ Container(height: 16, width: 50, decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(10))),
+ Container(height: 16, width: 45, decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(10))),
+ Container(height: 16, width: 45, decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(10))),
],
),
],
@@ -1306,32 +960,11 @@ class _DashboardScreenForDependetState extends State
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
- Container(
- width: 84,
- height: 84,
- decoration: BoxDecoration(
- color: Colors.white,
- borderRadius: BorderRadius.circular(18),
- ),
- ),
+ Container(width: 84, height: 84, decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(18))),
const SizedBox(height: 8),
- Container(
- width: 65,
- height: 11,
- decoration: BoxDecoration(
- color: Colors.white,
- borderRadius: BorderRadius.circular(4),
- ),
- ),
+ Container(width: 65, height: 11, decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(4))),
const SizedBox(height: 4),
- Container(
- width: 42,
- height: 11,
- decoration: BoxDecoration(
- color: Colors.white,
- borderRadius: BorderRadius.circular(4),
- ),
- ),
+ Container(width: 42, height: 11, decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(4))),
],
),
);
diff --git a/lib/ui/landing/wala_plus_retailer_details_screen.dart b/lib/ui/landing/wala_plus_retailer_details_screen.dart
index c46863a..cc15b04 100644
--- a/lib/ui/landing/wala_plus_retailer_details_screen.dart
+++ b/lib/ui/landing/wala_plus_retailer_details_screen.dart
@@ -1,7 +1,9 @@
+import 'dart:developer';
import 'dart:ui' as ui;
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_html/flutter_html.dart';
+import 'package:flutter_inappwebview/flutter_inappwebview.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:mohem_flutter_app/api/offers_and_discounts_api_client.dart';
import 'package:mohem_flutter_app/app_state/app_state.dart';
@@ -11,22 +13,20 @@ import 'package:mohem_flutter_app/classes/utils.dart';
import 'package:mohem_flutter_app/config/routes.dart';
import 'package:mohem_flutter_app/extensions/int_extensions.dart';
import 'package:mohem_flutter_app/extensions/string_extensions.dart';
+import 'package:mohem_flutter_app/extensions/widget_extensions.dart';
import 'package:mohem_flutter_app/generated/locale_keys.g.dart';
import 'package:mohem_flutter_app/models/offers_and_discounts/wala_plus_retailer_model.dart';
import 'package:mohem_flutter_app/widgets/app_bar_widget.dart';
import 'package:mohem_flutter_app/widgets/app_in_app_webview_screen.dart';
import 'package:mohem_flutter_app/widgets/button/default_button.dart';
+import 'package:mohem_flutter_app/widgets/info_chip_widget.dart';
import 'package:url_launcher/url_launcher.dart';
class WalaPlusRetailerDetailsScreen extends StatefulWidget {
final WalaPlusRetailerModel? retailer;
final String? retailerId;
- const WalaPlusRetailerDetailsScreen({
- Key? key,
- this.retailer,
- this.retailerId,
- }) : super(key: key);
+ const WalaPlusRetailerDetailsScreen({Key? key, this.retailer, this.retailerId}) : super(key: key);
@override
State createState() => _WalaPlusRetailerDetailsScreenState();
@@ -81,10 +81,7 @@ class _WalaPlusRetailerDetailsScreenState extends State _openInAppBrowser(String url) async {
+ Future _openInAppBrowser(String url, {String? offerType}) async {
String title = _retailer?.name ?? LocaleKeys.offerDetails.tr();
+ bool shouldStartScanning = offerType?.trim().toLowerCase() == "discount";
await Navigator.push(
context,
- MaterialPageRoute(
- builder: (BuildContext context) => AppInAppWebViewScreen(
- url: url,
- title: title,
- ),
- ),
+ MaterialPageRoute(builder: (BuildContext context) => AppInAppWebViewScreen(url: url, title: title, onPageLoaded: shouldStartScanning ? _clickStartScanningButton : null)),
+ );
+ }
+
+ // Vue's @click="startScanning()" is a component method, not something on window,
+ // so we find the actual button by its text and click it (lets Vue's own handler fire).
+ // Vue may still be mounting when onLoadStop fires, so poll briefly before giving up.
+ // Only called for offerType "discount" - "promo" offers just show a promo code, no scanner.
+ void _clickStartScanningButton(InAppWebViewController controller) {
+ controller.evaluateJavascript(
+ source: r"""
+ (function() {
+ var attempts = 0;
+ var maxAttempts = 10; // ~0.5s at 50ms
+ var timer = setInterval(function() {
+ attempts++;
+ var buttons = document.querySelectorAll('button');
+ for (var i = 0; i < buttons.length; i++) {
+ var text = (buttons[i].textContent || '').replace(/\s+/g, ' ').trim();
+ if (text.indexOf('Start Scanning') !== -1) {
+ buttons[i].click();
+ clearInterval(timer);
+ return;
+ }
+ }
+ if (attempts >= maxAttempts) {
+ clearInterval(timer);
+ }
+ }, 50);
+ })();
+ """,
);
}
@@ -160,16 +183,14 @@ class _WalaPlusRetailerDetailsScreenState extends State offers = _retailer?.offers ?? [];
+ log("offers.length: ${offers.length}");
+
if (offers.length > 1) {
_showOffersBottomSheet(offers);
} else if (offers.length == 1) {
WalaPlusOfferModel singleOffer = offers.first;
- String offerId = (singleOffer.id != null && singleOffer.id!.isNotEmpty)
- ? singleOffer.id!
- : (_retailer?.id ?? _effectiveRetailerId ?? "");
- String offerType = (singleOffer.type != null && singleOffer.type!.isNotEmpty)
- ? singleOffer.type!
- : (_retailer?.offerType ?? "promo");
+ String offerId = (singleOffer.id != null && singleOffer.id!.isNotEmpty) ? singleOffer.id! : (_retailer?.id ?? _effectiveRetailerId ?? "");
+ String offerType = (singleOffer.type != null && singleOffer.type!.isNotEmpty) ? singleOffer.type! : (_retailer?.offerType ?? "promo");
_redeemOffer(offerId: offerId, offerType: offerType);
} else {
String offerId = _retailer?.id ?? _effectiveRetailerId ?? "";
@@ -194,31 +215,21 @@ class _WalaPlusRetailerDetailsScreenState extends State? res = await OffersAndDiscountsApiClient().redeemWalaPlusOffer(
- offerId: offerId,
- offerType: offerType,
- context: context,
- );
+ Map? res = await OffersAndDiscountsApiClient().redeemWalaPlusOffer(offerId: offerId, offerType: offerType, context: context);
Utils.hideLoading(context);
String? transactionUrl;
if (res != null) {
- transactionUrl = res['Transaction_Url'] ??
- res['TransactionUrl'] ??
- res['transaction_url'] ??
- res['transactionUrl'] ??
- res['Url'] ??
- res['url'];
+ transactionUrl = res['Transaction_Url'] ?? res['TransactionUrl'] ?? res['transaction_url'] ?? res['transactionUrl'] ?? res['Url'] ?? res['url'];
}
+ log("transactionUrl: ${transactionUrl.toString()}");
+
if (transactionUrl != null && transactionUrl.trim().isNotEmpty) {
- await _openInAppBrowser(transactionUrl.trim());
+ await _openInAppBrowser(transactionUrl.trim(), offerType: offerType);
} else {
- String? error = res?['Error']?.toString() ??
- res?['error']?.toString() ??
- res?['ErrorMessage']?.toString() ??
- res?['message']?.toString();
+ String? error = res?['Error']?.toString() ?? res?['error']?.toString() ?? res?['ErrorMessage']?.toString() ?? res?['message']?.toString();
if (error != null && error.isNotEmpty && error != "null") {
Utils.showToast(error);
} else {
@@ -249,28 +260,14 @@ class _WalaPlusRetailerDetailsScreenState extends State(Color(0xFF168A89)),
- ),
- Expanded(
- child: SingleChildScrollView(
- physics: const BouncingScrollPhysics(),
- padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14),
- child: Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- Container(
- decoration: BoxDecoration(
- color: Colors.white,
- borderRadius: BorderRadius.circular(20),
- boxShadow: [
- BoxShadow(
- color: const Color(0xff000000).withOpacity(.05),
- blurRadius: 14,
- offset: const Offset(0, 4),
- ),
- ],
- ),
- padding: const EdgeInsets.all(16),
- child: Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- // 1. Top Banner Image with rounded corners
- ClipRRect(
- borderRadius: BorderRadius.circular(16),
- child: SizedBox(
- height: 190,
- width: double.infinity,
- child: (_retailer?.displayImage != null && _retailer!.displayImage!.isNotEmpty)
- ? Image.network(
- _retailer!.displayImage!,
- fit: BoxFit.cover,
- errorBuilder: (_, __, ___) => Container(
- color: const Color(0xFFF3F4F6),
- child: const Icon(Icons.broken_image, size: 48, color: Color(0xFF9CA3AF)),
- ),
- )
- : Container(
- color: const Color(0xFFF3F4F6),
- child: const Icon(Icons.image_outlined, size: 48, color: Color(0xFF9CA3AF)),
- ),
- ),
- ),
- 14.height,
-
- // 2. Brand Strip (Human Resources - Retailer Logo - Dr Sulaiman Al Habib)
- _buildBrandStrip(isAr),
- 18.height,
-
- // 3. Retailer Name (Centered, Bold) & Offer Title Subtitle
- Center(
- child: (_retailer?.name ?? "").toText22(
- isBold: true,
- isCentered: true,
- color: const Color(0xFF1E293B),
- ),
- ),
- if (_retailer?.offerTitle != null &&
- _retailer!.offerTitle!.trim().isNotEmpty &&
- _retailer!.offerTitle!.trim() != _retailer!.name?.trim()) ...[
- 6.height,
- Center(
- child: _cleanHtml(_retailer!.offerTitle!).toText16(
- isCentered: true,
- color: const Color(0xFF168A89),
+ body:
+ (_isLoading && !_hasDetailsData)
+ ? Center(child: Image.asset("assets/images/logos/loading_mohemm_logo.gif", height: 96.0, width: 96.0))
+ : Column(
+ children: [
+ if (_isLoading && _hasDetailsData) const LinearProgressIndicator(minHeight: 2.5, backgroundColor: Colors.transparent, valueColor: AlwaysStoppedAnimation(Color(0xFF168A89))),
+ Expanded(
+ child: SingleChildScrollView(
+ physics: const BouncingScrollPhysics(),
+ padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Container(
+ decoration: BoxDecoration(
+ color: Colors.white,
+ borderRadius: BorderRadius.circular(20),
+ boxShadow: [BoxShadow(color: const Color(0xff000000).withOpacity(.05), blurRadius: 14, offset: const Offset(0, 4))],
+ ),
+ padding: const EdgeInsets.all(16),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ // 1. Top Banner Image with rounded corners
+ ClipRRect(
+ borderRadius: BorderRadius.circular(16),
+ child: SizedBox(
+ height: 190,
+ width: double.infinity,
+ child:
+ (_retailer?.displayImage != null && _retailer!.displayImage!.isNotEmpty)
+ ? Image.network(
+ _retailer!.displayImage!,
+ fit: BoxFit.cover,
+ errorBuilder: (_, __, ___) => Container(color: const Color(0xFFF3F4F6), child: const Icon(Icons.broken_image, size: 48, color: Color(0xFF9CA3AF))),
+ )
+ : Container(color: const Color(0xFFF3F4F6), child: const Icon(Icons.image_outlined, size: 48, color: Color(0xFF9CA3AF))),
),
),
+ 14.height,
+
+ // 2. Brand Strip (Human Resources - Retailer Logo - Dr Sulaiman Al Habib)
+ _buildBrandStrip(isAr),
+ // 18.height,
+
+ // 3. Retailer Name (Centered, Bold) & Offer Title Subtitle
+ (_retailer?.name ?? "").toText22(isBold: true, isCentered: false, color: const Color(0xFF1E293B)),
+ // if (_retailer?.offerTitle != null && _retailer!.offerTitle!.trim().isNotEmpty && _retailer!.offerTitle!.trim() != _retailer!.name?.trim()) ...[
+ // 6.height,
+ // Center(child: _cleanHtml(_retailer!.offerTitle!).toText16(isCentered: true, color: const Color(0xFF168A89))),
+ // ],
+ 18.height,
+
+ // 4. 3 Info Badges Row (Store Type, Validity, City)
+ _buildInfoBadgesRow(isAr),
+ 10.height,
+
+ // 5. Description Section (parsed as HTML)
+ // "${LocaleKeys.description.tr()}:".toText16(isBold: true, color: const Color(0xFF1E293B)),
+ // 8.height,
+ // Html(
+ // data: _getHowToUseHtml(isAr),
+ // style: {"body": Style(fontSize: FontSize(14.0), color: const Color(0xFF4B5563))},
+ // onLinkTap: (String? url, Map attributes, _) async {
+ // if (url != null && url.isNotEmpty) {
+ // try {
+ // Uri uri = Uri.parse(url);
+ // if (!uri.hasScheme) {
+ // uri = Uri.parse("https://$url");
+ // }
+ // await launchUrl(uri, mode: LaunchMode.externalApplication);
+ // } catch (e) {
+ // debugPrint("Error launching url: $e");
+ // }
+ // }
+ // },
+ // ),
],
- 18.height,
-
- // 4. 3 Info Badges Row (Store Type, Validity, City)
- _buildInfoBadgesRow(isAr),
- 22.height,
-
- // 5. Description Section (parsed as HTML)
- "${LocaleKeys.description.tr()}:".toText16(
- isBold: true,
- color: const Color(0xFF1E293B),
- ),
- 8.height,
- Html(
- data: _getHowToUseHtml(isAr),
- style: {
- "body": Style(
- fontSize: FontSize(14.0),
- color: const Color(0xFF4B5563),
- ),
- },
- onLinkTap: (String? url, Map attributes, _) async {
- if (url != null && url.isNotEmpty) {
- try {
- Uri uri = Uri.parse(url);
- if (!uri.hasScheme) {
- uri = Uri.parse("https://$url");
- }
- await launchUrl(uri, mode: LaunchMode.externalApplication);
- } catch (e) {
- debugPrint("Error launching url: $e");
- }
- }
- },
- ),
- ],
+ ),
),
- ),
- // 6. Locations Section (below description)
- _buildLocationsSection(isAr),
+ // 6. Locations Section (below description)
+ _buildLocationsSection(isAr),
- // 7. Contact Information (Email & Phone Number)
- _buildContactSection(isAr),
+ // 7. Contact Information (Email & Phone Number)
+ _buildContactSection(isAr),
- 16.height,
- ],
+ 16.height,
+ ],
+ ),
),
),
- ),
- // 7. Bottom Action Button
- Container(
- padding: const EdgeInsets.fromLTRB(16, 10, 16, 20),
- color: Colors.white,
- child: SizedBox(
- width: double.infinity,
- height: 50,
- child: DefaultButton(
- LocaleKeys.useOffer.tr(),
- _isRedeeming ? null : _onUseOffer,
- height: 50,
- borderRadius: 12,
- fontSize: 16,
- ),
+ // 7. Bottom Action Button
+ Container(
+ padding: const EdgeInsets.fromLTRB(16, 10, 16, 20),
+ color: Colors.white,
+ child: SizedBox(width: double.infinity, height: 50, child: DefaultButton(LocaleKeys.useOffer.tr(), _isRedeeming ? null : _onUseOffer, height: 50, borderRadius: 12, fontSize: 16)),
),
- ),
- ],
- ),
+ ],
+ ),
);
}
@@ -597,69 +505,48 @@ class _WalaPlusRetailerDetailsScreenState extends State const SizedBox(width: 36, height: 36),
- ),
+ child: Image.network(logoUrl, height: 36, width: 36, fit: BoxFit.contain, errorBuilder: (_, __, ___) => const SizedBox(width: 36, height: 36)),
)
else
const SizedBox(width: 36, height: 36),
// Right: Mazaya Brand Logo
- SvgPicture.asset(
- "assets/icons/mazaya_brand.svg",
- height: 48,
- fit: BoxFit.contain,
- ),
+ SvgPicture.asset("assets/icons/mazaya_brand.svg", height: 48, fit: BoxFit.contain),
],
);
}
Widget _buildInfoBadgesRow(bool isAr) {
- String storeTypeText = (_retailer?.type?.toLowerCase() == 'online')
- ? LocaleKeys.online.tr()
- : LocaleKeys.inStore.tr();
+ bool isOnline = (_retailer?.type?.toLowerCase() == 'online');
+ String storeTypeText = isOnline ? LocaleKeys.online.tr() : LocaleKeys.inStore.tr();
- IconData storeIcon = (_retailer?.type?.toLowerCase() == 'online')
- ? Icons.language_rounded
- : Icons.storefront_rounded;
+ IconData storeIcon = isOnline ? Icons.language_rounded : Icons.storefront_rounded;
String cityText = _getDisplayCity(isAr);
String validityText = _retailer?.getDisplayValidity() ?? LocaleKeys.offerValid.tr();
- return Row(
+ return Wrap(
+ spacing: 12,
+ runSpacing: 10,
children: [
- Expanded(
- child: _buildInfoCard(
- icon: storeIcon,
- label: storeTypeText,
- ),
+ InfoChipWidget(
+ text: storeTypeText,
+ icon: storeIcon,
+ backgroundColor: isOnline ? const Color(0xFFEFF6FF) : const Color(0xFFF5F3FF),
+ textColor: isOnline ? const Color(0xFF2563EB) : const Color(0xFF7C3AED),
),
- 12.width,
- Expanded(
- child: _buildInfoCard(
- icon: Icons.check_circle_outline_rounded,
- label: validityText,
- ),
- ),
- 12.width,
- Expanded(
- child: _buildInfoCard(
- icon: Icons.location_on_rounded,
- label: cityText,
- ),
+ InfoChipWidget(text: validityText, backgroundColor: const Color(0xFFE6F7F0), textColor: const Color(0xFF00A651)),
+ InfoChipWidget(
+ text: cityText,
+ svgIcon: "assets/icons/location.svg",
+ isBold: false,
+ backgroundColor: const Color(0xFFF3F4F6),
+ textColor: const Color(0xFF4B5563),
+ iconColor: const Color(0xFF64748B),
+ iconTextGap: 2.5,
),
],
);
@@ -696,29 +583,6 @@ class _WalaPlusRetailerDetailsScreenState extends State]*>').hasMatch(s);
@@ -884,9 +737,8 @@ class _WalaPlusRetailerDetailsScreenState extends State previewLocations =
- totalCount <= 2 ? locations : locations.take(2).toList();
+ int totalCount = locations.length;
+ List previewLocations = totalCount <= 2 ? locations : locations.take(2).toList();
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
@@ -900,31 +752,19 @@ class _WalaPlusRetailerDetailsScreenState extends State 2)
@@ -936,16 +776,9 @@ class _WalaPlusRetailerDetailsScreenState extends State 2) ...[
- 10.height,
- InkWell(
- onTap: () => _showAllLocationsBottomSheet(locations, isAr),
- borderRadius: BorderRadius.circular(16),
- child: Container(
- padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 14),
- decoration: BoxDecoration(
- color: Colors.white,
- borderRadius: BorderRadius.circular(16),
- boxShadow: [
- BoxShadow(
- color: const Color(0xff000000).withOpacity(.05),
- blurRadius: 10,
- offset: const Offset(0, 3),
- ),
- ],
- ),
- child: Row(
- children: [
- Container(
- padding: const EdgeInsets.all(9),
- decoration: BoxDecoration(
- color: const Color(0xFF168A89).withOpacity(0.08),
- shape: BoxShape.circle,
- ),
- child: const Icon(Icons.map_outlined, color: Color(0xFF168A89), size: 18),
- ),
- 12.width,
- Expanded(
- child: Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- LocaleKeys.showAllLocations.tr(args: ['$totalCount']).toText14(
- isBold: true,
- color: const Color(0xFF168A89),
- ),
- 2.height,
- LocaleKeys.browseAllBranches.tr(args: ['$totalCount']).toText11(
- color: const Color(0xFF64748B),
- ),
- ],
- ),
- ),
- Icon(
- isAr ? Icons.arrow_back_ios_rounded : Icons.arrow_forward_ios_rounded,
- size: 14,
- color: const Color(0xFF168A89),
- ),
- ],
- ),
- ),
- ),
- ],
+ // if (totalCount > 2) ...[
+ // 10.height,
+ // InkWell(
+ // onTap: () => _showAllLocationsBottomSheet(locations, isAr),
+ // borderRadius: BorderRadius.circular(16),
+ // child: Container(
+ // padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 14),
+ // decoration: BoxDecoration(
+ // color: Colors.white,
+ // borderRadius: BorderRadius.circular(16),
+ // boxShadow: [BoxShadow(color: const Color(0xff000000).withOpacity(.05), blurRadius: 10, offset: const Offset(0, 3))],
+ // ),
+ // child: Row(
+ // children: [
+ // Container(
+ // padding: const EdgeInsets.all(9),
+ // decoration: BoxDecoration(color: const Color(0xFF168A89).withOpacity(0.08), shape: BoxShape.circle),
+ // child: const Icon(Icons.map_outlined, color: Color(0xFF168A89), size: 18),
+ // ),
+ // 12.width,
+ // Expanded(
+ // child: Column(
+ // crossAxisAlignment: CrossAxisAlignment.start,
+ // children: [
+ // LocaleKeys.showAllLocations.tr(args: ['$totalCount']).toText14(isBold: true, color: const Color(0xFF168A89)),
+ // 2.height,
+ // LocaleKeys.browseAllBranches.tr(args: ['$totalCount']).toText11(color: const Color(0xFF64748B)),
+ // ],
+ // ),
+ // ),
+ // Icon(isAr ? Icons.arrow_back_ios_rounded : Icons.arrow_forward_ios_rounded, size: 14, color: const Color(0xFF168A89)),
+ // ],
+ // ),
+ // ),
+ // ),
+ // ],
],
);
}
@@ -1031,120 +846,91 @@ class _WalaPlusRetailerDetailsScreenState extends State[BoxShadow(color: const Color(0xff000000).withOpacity(.05), blurRadius: 10, offset: const Offset(0, 3))],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
- Row(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- crossAxisAlignment: CrossAxisAlignment.center,
- children: [
- // City Pill Badge
- if (cityText.isNotEmpty)
- Container(
- padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 3.5),
- decoration: BoxDecoration(
- color: const Color(0xFF168A89).withOpacity(0.08),
- borderRadius: BorderRadius.circular(6),
- ),
+ // Row(
+ // mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ // crossAxisAlignment: CrossAxisAlignment.center,
+ // children: [
+ // // City Pill Badge
+ // if (cityText.isNotEmpty)
+ // Container(
+ // padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 3.5),
+ // decoration: BoxDecoration(color: const Color(0xFF168A89).withOpacity(0.08), borderRadius: BorderRadius.circular(6)),
+ // child: Row(
+ // mainAxisSize: MainAxisSize.min,
+ // children: [const Icon(Icons.location_on, size: 12, color: Color(0xFF168A89)), const SizedBox(width: 4), cityText.toText11(isBold: true, color: const Color(0xFF168A89))],
+ // ),
+ // )
+ // else
+ // const SizedBox.shrink(),
+ // ],
+ // ),
+ // 10.height,
+ // Branch title & pin icon
+ InkWell(
+ onTap: () => _openMapForLocation(loc),
+ borderRadius: BorderRadius.circular(8),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ Expanded(
child: Row(
- mainAxisSize: MainAxisSize.min,
+ crossAxisAlignment: CrossAxisAlignment.start,
children: [
- const Icon(Icons.location_on, size: 12, color: Color(0xFF168A89)),
- const SizedBox(width: 4),
- cityText.toText11(
- isBold: true,
- color: const Color(0xFF168A89),
+ Container(
+ margin: const EdgeInsets.only(top: 2),
+ width: 36,
+ height: 36,
+ alignment: Alignment.center,
+ decoration: const BoxDecoration(color: Color(0xFFF3F4F6), shape: BoxShape.circle),
+ child: SvgPicture.asset("assets/icons/location.svg", width: 16, height: 16),
),
- ],
- ),
- )
- else
- const SizedBox.shrink(),
-
- // Directions / Map action button
- InkWell(
- onTap: () => _openMapForLocation(loc),
- borderRadius: BorderRadius.circular(8),
- child: Container(
- padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 6),
- decoration: BoxDecoration(
- gradient: const LinearGradient(
- transform: GradientRotation(.83),
- begin: Alignment.topRight,
- end: Alignment.bottomLeft,
- colors: [MyColors.gradiantEndColor, MyColors.gradiantStartColor],
- ),
- borderRadius: BorderRadius.circular(8),
- ),
- child: Row(
- mainAxisSize: MainAxisSize.min,
- children: [
- const Icon(Icons.directions_outlined, color: Colors.white, size: 14),
- const SizedBox(width: 4),
- LocaleKeys.viewOnMap.tr().toText11(
- color: Colors.white,
- isBold: true,
+ 8.width,
+ Expanded(
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ primaryTitle.toText14(isBold: true, color: const Color(0xFF1E293B), maxlines: 2),
+ if (secondaryText != null) ...[2.height, secondaryText.toText12(color: const Color(0xFF057958), maxLine: 1)],
+ ],
+ ),
),
],
),
),
- ),
- ],
- ),
- 10.height,
- // Branch title & pin icon
- Row(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- Container(
- margin: const EdgeInsets.only(top: 2),
- padding: const EdgeInsets.all(6),
- decoration: BoxDecoration(
- color: const Color(0xFF168A89).withOpacity(0.08),
- shape: BoxShape.circle,
- ),
- child: const Icon(Icons.store_mall_directory_rounded, size: 14, color: Color(0xFF168A89)),
- ),
- 8.width,
- Expanded(
- child: Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- primaryTitle.toText14(
- isBold: true,
- color: const Color(0xFF1E293B),
- maxlines: 2,
- ),
- if (secondaryText != null) ...[
- 2.height,
- secondaryText.toText12(
- color: const Color(0xFF64748B),
- maxLine: 1,
- ),
- ],
- ],
- ),
- ),
- ],
+
+ 12.width,
+ SvgPicture.asset("assets/icons/location.svg", width: 18, height: 18),
+
+ // Container(
+ // padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 6),
+ // decoration: BoxDecoration(
+ // gradient: const LinearGradient(
+ // transform: GradientRotation(.83),
+ // begin: Alignment.topRight,
+ // end: Alignment.bottomLeft,
+ // colors: [MyColors.gradiantEndColor, MyColors.gradiantStartColor],
+ // ),
+ // borderRadius: BorderRadius.circular(8),
+ // ),
+ // child: Row(
+ // mainAxisSize: MainAxisSize.min,
+ // children: [const Icon(Icons.directions_outlined, color: Colors.white, size: 14), const SizedBox(width: 4), LocaleKeys.viewOnMap.tr().toText11(color: Colors.white, isBold: true)],
+ // ),
+ // ),
+ ],
+ ),
),
],
),
@@ -1160,8 +946,7 @@ class _WalaPlusRetailerDetailsScreenState extends State sortedCities = cityCounts.keys.toList()
- ..sort((a, b) => (cityCounts[b] ?? 0).compareTo(cityCounts[a] ?? 0));
+ List sortedCities = cityCounts.keys.toList()..sort((a, b) => (cityCounts[b] ?? 0).compareTo(cityCounts[a] ?? 0));
String selectedCity = "ALL";
String searchQuery = "";
@@ -1174,48 +959,35 @@ class _WalaPlusRetailerDetailsScreenState extends State filtered = locations.where((loc) {
- String c = loc.getLocalizedCity(isAr);
- if (c.isEmpty) c = loc.city ?? "";
- if (selectedCity != "ALL" && c.trim() != selectedCity) {
- return false;
- }
- if (searchQuery.isNotEmpty) {
- String q = searchQuery.toLowerCase();
- String addr = (loc.address ?? "").toLowerCase();
- String cityEn = (loc.cityNameEn ?? "").toLowerCase();
- String cityAr = (loc.cityNameAr ?? "").toLowerCase();
- String genericCity = (loc.city ?? "").toLowerCase();
- if (!addr.contains(q) && !cityEn.contains(q) && !cityAr.contains(q) && !genericCity.contains(q)) {
- return false;
- }
- }
- return true;
- }).toList();
+ List filtered =
+ locations.where((loc) {
+ String c = loc.getLocalizedCity(isAr);
+ if (c.isEmpty) c = loc.city ?? "";
+ if (selectedCity != "ALL" && c.trim() != selectedCity) {
+ return false;
+ }
+ if (searchQuery.isNotEmpty) {
+ String q = searchQuery.toLowerCase();
+ String addr = (loc.address ?? "").toLowerCase();
+ String cityEn = (loc.cityNameEn ?? "").toLowerCase();
+ String cityAr = (loc.cityNameAr ?? "").toLowerCase();
+ String genericCity = (loc.city ?? "").toLowerCase();
+ if (!addr.contains(q) && !cityEn.contains(q) && !cityAr.contains(q) && !genericCity.contains(q)) {
+ return false;
+ }
+ }
+ return true;
+ }).toList();
return Container(
- constraints: BoxConstraints(
- maxHeight: MediaQuery.of(sheetContext).size.height * 0.85,
- ),
- decoration: const BoxDecoration(
- color: Color(0xFFF8F9FA),
- borderRadius: BorderRadius.vertical(top: Radius.circular(24)),
- ),
+ constraints: BoxConstraints(maxHeight: MediaQuery.of(sheetContext).size.height * 0.85),
+ decoration: const BoxDecoration(color: Color(0xFFF8F9FA), borderRadius: BorderRadius.vertical(top: Radius.circular(24))),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
12.height,
- Center(
- child: Container(
- width: 44,
- height: 4.5,
- decoration: BoxDecoration(
- color: const Color(0xFFCBD5E1),
- borderRadius: BorderRadius.circular(2.5),
- ),
- ),
- ),
+ Center(child: Container(width: 44, height: 4.5, decoration: BoxDecoration(color: const Color(0xFFCBD5E1), borderRadius: BorderRadius.circular(2.5)))),
12.height,
Padding(
padding: const EdgeInsets.symmetric(horizontal: 20),
@@ -1227,14 +999,9 @@ class _WalaPlusRetailerDetailsScreenState extends State 10.height,
+ itemBuilder: (BuildContext _, int index) {
+ return _buildLocationCard(filtered[index], isAr);
+ },
),
- )
- : ListView.separated(
- shrinkWrap: true,
- physics: const BouncingScrollPhysics(),
- padding: const EdgeInsets.fromLTRB(20, 16, 20, 24),
- itemCount: filtered.length,
- separatorBuilder: (_, __) => 10.height,
- itemBuilder: (BuildContext _, int index) {
- return _buildLocationCard(filtered[index], isAr);
- },
- ),
),
],
),
@@ -1401,10 +1147,7 @@ class _WalaPlusRetailerDetailsScreenState extends State _openMapForLocation(WalaPlusRetailerLocation loc) async {
- bool hasCoordinates = loc.latitude != null &&
- loc.longitude != null &&
- loc.latitude != 0 &&
- loc.longitude != 0;
+ bool hasCoordinates = loc.latitude != null && loc.longitude != null && loc.latitude != 0 && loc.longitude != 0;
Uri googleMapsUrl;
if (hasCoordinates) {
@@ -1440,10 +1183,7 @@ class _WalaPlusRetailerDetailsScreenState extends State {
}
}
},
+ onLoadStop: (InAppWebViewController controller, WebUri? url) async {
+ widget.onPageLoaded?.call(controller);
+ },
),
if (_progress < 1.0)
LinearProgressIndicator(
diff --git a/lib/widgets/info_chip_widget.dart b/lib/widgets/info_chip_widget.dart
new file mode 100644
index 0000000..67bd374
--- /dev/null
+++ b/lib/widgets/info_chip_widget.dart
@@ -0,0 +1,66 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_svg/flutter_svg.dart';
+import 'package:mohem_flutter_app/extensions/string_extensions.dart';
+
+/// Small pill-shaped badge used for retailer/offer meta info (type, validity, location, usage, ...).
+/// Flexible via optional params - every call only needs to override what differs from the given defaults.
+class InfoChipWidget extends StatelessWidget {
+ final String text;
+ final Color backgroundColor;
+ final Color textColor;
+ final Color? iconColor;
+ final IconData? icon;
+ final String? svgIcon;
+ final bool isBold;
+ final double iconSize;
+ final double iconTextGap;
+ final double horizontalPadding;
+ final double verticalPadding;
+ final double borderRadius;
+ final Color? borderColor;
+ final double borderWidth;
+
+ const InfoChipWidget({
+ Key? key,
+ required this.text,
+ required this.backgroundColor,
+ required this.textColor,
+ this.iconColor,
+ this.icon,
+ this.svgIcon,
+ this.isBold = true,
+ this.iconSize = 10,
+ this.iconTextGap = 3,
+ this.horizontalPadding = 7,
+ this.verticalPadding = 2.5,
+ this.borderRadius = 10,
+ this.borderColor,
+ this.borderWidth = 1,
+ }) : super(key: key);
+
+ @override
+ Widget build(BuildContext context) {
+ return Container(
+ padding: EdgeInsets.symmetric(horizontal: horizontalPadding, vertical: verticalPadding),
+ decoration: BoxDecoration(
+ color: backgroundColor,
+ borderRadius: BorderRadius.circular(borderRadius),
+ border: Border.all(color: (borderColor ?? textColor).withValues(alpha: 0.7), width: borderWidth),
+ ),
+ child:
+ (icon == null && svgIcon == null)
+ ? text.toText10(isBold: isBold, color: textColor)
+ : Row(
+ mainAxisSize: MainAxisSize.min,
+ children: [_buildIcon(), SizedBox(width: iconTextGap), text.toText10(isBold: isBold, color: textColor)],
+ ),
+ );
+ }
+
+ Widget _buildIcon() {
+ if (svgIcon != null) {
+ return SvgPicture.asset(svgIcon!, width: iconSize, height: iconSize, colorFilter: ColorFilter.mode(iconColor ?? textColor, BlendMode.srcIn));
+ }
+ return Icon(icon, size: iconSize, color: iconColor ?? textColor);
+ }
+}