From b6670289b12de5707e8f4a642835047c7318493c Mon Sep 17 00:00:00 2001 From: devmirza121 Date: Mon, 7 Feb 2022 11:09:15 +0300 Subject: [PATCH] Dashboard API's 1.2 --- lib/api/dashboard_api_client.dart | 22 ++ lib/api/login_api_client.dart | 1 + lib/extensions/string_extensions.dart | 8 + lib/extensions/widget_extensions.dart | 15 +- lib/main.dart | 6 +- lib/models/dashboard/list_menu.dart | 39 ++ lib/models/dashboard/menu_entries.dart | 60 +++ lib/models/dashboard/menus.dart | 10 + lib/models/generic_response_model.dart | 23 +- lib/provider/dashboard_provider_model.dart | 142 +++++++- lib/ui/landing/dashboard_screen.dart | 341 +++++++----------- lib/ui/landing/widget/menus_widget.dart | 138 +++++++ lib/ui/landing/widget/services_widget.dart | 147 ++++++++ lib/widgets/Updater.dart | 39 ++ .../shimmer/dashboard_shimmer_widget.dart | 109 +++++- 15 files changed, 864 insertions(+), 236 deletions(-) create mode 100644 lib/models/dashboard/list_menu.dart create mode 100644 lib/models/dashboard/menu_entries.dart create mode 100644 lib/models/dashboard/menus.dart create mode 100644 lib/ui/landing/widget/menus_widget.dart create mode 100644 lib/ui/landing/widget/services_widget.dart create mode 100644 lib/widgets/Updater.dart diff --git a/lib/api/dashboard_api_client.dart b/lib/api/dashboard_api_client.dart index ad513e9..b98a777 100644 --- a/lib/api/dashboard_api_client.dart +++ b/lib/api/dashboard_api_client.dart @@ -67,4 +67,26 @@ class DashbaordApiClient { return responseData; }, url, postParams); } + + //Menus List + Future getListMenu() async { + String url = "${ApiConsts.erpRest}GET_MENU"; + Map postParams = {}; + postParams.addAll(AppState().postParamsJson); + return await ApiClient().postJsonForObject((json) { + GenericResponseModel responseData = GenericResponseModel.fromJson(json); + return responseData; + }, url, postParams); + } + + //GET_MENU_ENTRIES + Future getGetMenuEntries() async { + String url = "${ApiConsts.erpRest}GET_MENU_ENTRIES"; + Map postParams = {"P_SELECTED_RESP_ID": -999,"P_MENU_TYPE":"E"}; + postParams.addAll(AppState().postParamsJson); + return await ApiClient().postJsonForObject((json) { + GenericResponseModel responseData = GenericResponseModel.fromJson(json); + return responseData; + }, url, postParams); + } } diff --git a/lib/api/login_api_client.dart b/lib/api/login_api_client.dart index fb6bb80..86b7ace 100644 --- a/lib/api/login_api_client.dart +++ b/lib/api/login_api_client.dart @@ -55,6 +55,7 @@ class LoginApiClient { AppState().postParamsObject?.pSessionId = responseData.pSESSIONID; AppState().postParamsObject?.pUserName = AppState().getUserName; AppState().postParamsObject?.pSelectedEmployeeNumber = AppState().getUserName; + return responseData; }, url, postParams); } diff --git a/lib/extensions/string_extensions.dart b/lib/extensions/string_extensions.dart index 9db4829..5d4f8cd 100644 --- a/lib/extensions/string_extensions.dart +++ b/lib/extensions/string_extensions.dart @@ -2,6 +2,14 @@ import 'package:flutter/cupertino.dart'; import 'package:intl/intl.dart'; import 'package:mohem_flutter_app/classes/colors.dart'; +extension CapExtension on String { + String get toCamelCase => "${this[0].toUpperCase()}${this.substring(1)}"; + String get inCaps => '${this[0].toUpperCase()}${this.substring(1)}'; + String get allInCaps => this.toUpperCase(); + String get capitalizeFirstofEach => this.trim().length > 0 ? this.trim().toLowerCase().split(" ").map((str) => str.inCaps).join(" ") : ""; +} + + extension EmailValidator on String { Widget get toWidget => Text(this); diff --git a/lib/extensions/widget_extensions.dart b/lib/extensions/widget_extensions.dart index dc083ce..130c867 100644 --- a/lib/extensions/widget_extensions.dart +++ b/lib/extensions/widget_extensions.dart @@ -10,13 +10,14 @@ extension WidgetExtensions on Widget { Widget paddingOnly({double left = 0.0, double right = 0.0, double top = 0.0, double bottom = 0.0}) => Padding(padding: EdgeInsets.only(left: left, right: right, top: top, bottom: bottom), child: this); - Widget toShimmer() => Shimmer.fromColors( - baseColor: Color(0xffe8eff0), - highlightColor: Colors.white, - child: Container( - child: this, - color: Colors.white, - )); + Widget toShimmer({bool isShow=true}) =>isShow? Shimmer.fromColors( + baseColor: Color(0xffe8eff0), + highlightColor: Colors.white, + child: Container( + child: this, + color: Colors.white, + ), + ):Container(child: this,); Widget animatedSwither() => AnimatedSwitcher( duration: const Duration(milliseconds: 500), diff --git a/lib/main.dart b/lib/main.dart index 0b05d10..1146ff0 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -16,9 +16,9 @@ import 'config/routes.dart'; var logger = Logger( - filter: null, // Use the default LogFilter (-> only log in debug mode) - printer: PrettyPrinter(), // Use the PrettyPrinter to format and print log - output: null, // U + // filter: null, // Use the default LogFilter (-> only log in debug mode) + printer: PrettyPrinter(lineLength: 0), // Use the PrettyPrinter to format and print log + // output: null, // U ); diff --git a/lib/models/dashboard/list_menu.dart b/lib/models/dashboard/list_menu.dart new file mode 100644 index 0000000..ba327c2 --- /dev/null +++ b/lib/models/dashboard/list_menu.dart @@ -0,0 +1,39 @@ +class ListMenu { + ListMenu({ + this.menuId, + this.menuName, + this.menuType, + this.requestGroupId, + this.requestGroupName, + this.respId, + this.subMenuName, + }); + + int? menuId; + String? menuName; + String? menuType; + int? requestGroupId; + String? requestGroupName; + dynamic? respId; + String? subMenuName; + + factory ListMenu.fromJson(Map json) => ListMenu( + menuId: json["MENU_ID"] == null ? null : json["MENU_ID"], + menuName: json["MENU_NAME"] == null ? null : json["MENU_NAME"], + menuType: json["MENU_TYPE"] == null ? null : json["MENU_TYPE"], + requestGroupId: json["REQUEST_GROUP_ID"] == null ? null : json["REQUEST_GROUP_ID"], + requestGroupName: json["REQUEST_GROUP_NAME"] == null ? null : json["REQUEST_GROUP_NAME"], + respId: json["RESP_ID"], + subMenuName: json["SUB_MENU_NAME"] == null ? null : json["SUB_MENU_NAME"], + ); + + Map toJson() => { + "MENU_ID": menuId == null ? null : menuId, + "MENU_NAME": menuName == null ? null : menuName, + "MENU_TYPE": menuType == null ? null : menuType, + "REQUEST_GROUP_ID": requestGroupId == null ? null : requestGroupId, + "REQUEST_GROUP_NAME": requestGroupName == null ? null : requestGroupName, + "RESP_ID": respId, + "SUB_MENU_NAME": subMenuName == null ? null : subMenuName, + }; +} diff --git a/lib/models/dashboard/menu_entries.dart b/lib/models/dashboard/menu_entries.dart new file mode 100644 index 0000000..7678ad8 --- /dev/null +++ b/lib/models/dashboard/menu_entries.dart @@ -0,0 +1,60 @@ +class GetMenuEntriesList { + GetMenuEntriesList({ + this.addButton, + this.deleteButton, + this.entrySequence, + this.functionName, + this.icon, + this.lvl, + this.menuEntryType, + this.menuName, + this.parentMenuName, + this.prompt, + this.requestType, + this.updateButton, + }); + + String? addButton; + String? deleteButton; + int? entrySequence; + String? functionName; + String? icon; + int? lvl; + String? menuEntryType; + String? menuName; + String? parentMenuName; + String? prompt; + String? requestType; + String? updateButton; + + factory GetMenuEntriesList.fromJson(Map json) => GetMenuEntriesList( + addButton: json["ADD_BUTTON"] == null ? null : json["ADD_BUTTON"], + deleteButton: json["DELETE_BUTTON"] == null ? null : json["DELETE_BUTTON"], + entrySequence: json["ENTRY_SEQUENCE"] == null ? null : json["ENTRY_SEQUENCE"], + functionName: json["FUNCTION_NAME"] == null ? null : json["FUNCTION_NAME"], + icon: json["ICON"] == null ? null : json["ICON"], + lvl: json["LVL"] == null ? null : json["LVL"], + menuEntryType: json["MENU_ENTRY_TYPE"] == null ? null : json["MENU_ENTRY_TYPE"], + menuName: json["MENU_NAME"] == null ? null : json["MENU_NAME"], + parentMenuName: json["PARENT_MENU_NAME"] == null ? null : json["PARENT_MENU_NAME"], + prompt: json["PROMPT"] == null ? null : json["PROMPT"], + requestType: json["REQUEST_TYPE"] == null ? null : json["REQUEST_TYPE"], + updateButton: json["UPDATE_BUTTON"] == null ? null :json["UPDATE_BUTTON"], + ); + + Map toJson() => { + "ADD_BUTTON": addButton == null ? null :addButton, + "DELETE_BUTTON": deleteButton == null ? null : deleteButton, + "ENTRY_SEQUENCE": entrySequence == null ? null : entrySequence, + "FUNCTION_NAME": functionName == null ? null : functionName, + "ICON": icon == null ? null : icon, + "LVL": lvl == null ? null : lvl, + "MENU_ENTRY_TYPE": menuEntryType == null ? null : menuEntryType, + "MENU_NAME": menuName == null ? null : menuName, + "PARENT_MENU_NAME": parentMenuName == null ? null : parentMenuName, + "PROMPT": prompt == null ? null : prompt, + "REQUEST_TYPE": requestType == null ? null : requestType, + "UPDATE_BUTTON": updateButton == null ? null : updateButton, + }; +} + diff --git a/lib/models/dashboard/menus.dart b/lib/models/dashboard/menus.dart new file mode 100644 index 0000000..7b88a33 --- /dev/null +++ b/lib/models/dashboard/menus.dart @@ -0,0 +1,10 @@ +import 'package:mohem_flutter_app/models/dashboard/menu_entries.dart'; + +class Menus { + GetMenuEntriesList menuEntry; + List menuEntiesList; + + Menus(this.menuEntry, this.menuEntiesList); + + +} diff --git a/lib/models/generic_response_model.dart b/lib/models/generic_response_model.dart index 86552a0..8ac68bb 100644 --- a/lib/models/generic_response_model.dart +++ b/lib/models/generic_response_model.dart @@ -5,6 +5,8 @@ import 'dashboard/get_accrual_balances_list_model.dart'; import 'dashboard/get_attendance_tracking_list_model.dart'; import 'dashboard/get_open_missing_swipes_list_model.dart'; import 'dashboard/get_open_notifications_list.dart'; +import 'dashboard/list_menu.dart'; +import 'dashboard/menu_entries.dart'; import 'member_information_list_model.dart'; import 'privilege_list_model.dart'; @@ -103,7 +105,7 @@ class GenericResponseModel { List? getItemTypeNotificationsList; List? getItemTypesList; List? getLookupValuesList; - List? getMenuEntriesList; + List? getMenuEntriesList; List? getMoItemHistoryList; List? getMoNotificationBodyList; List? getNotificationButtonsList; @@ -169,7 +171,7 @@ class GenericResponseModel { String? listItemImagesDetails; String? listItemMaster; String? listMedicineDetails; - String? listMenu; + List? listMenu; String? listNewEmployees; String? listRadScreen; String? logInTokenID; @@ -198,7 +200,7 @@ class GenericResponseModel { String? pINFORMATION; int? pMBLID; String? pNUMOFSUBORDINATES; - String? pOPENNTFNUMBER; + int? pOPENNTFNUMBER; String? pQUESTION; int? pSESSIONID; String? pSchema; @@ -567,15 +569,13 @@ class GenericResponseModel { getAbsenceCollectionNotificationBodyList = json['GetAbsenceCollectionNotificationBodyList']; getAbsenceDffStructureList = json['GetAbsenceDffStructureList']; getAbsenceTransactionList = json['GetAbsenceTransactionList']; - getAccrualBalancesList= - json["GetAccrualBalancesList"] == null ? null : List.from(json["GetAccrualBalancesList"].map((x) => GetAccrualBalancesList.fromJson(x))); + getAccrualBalancesList = json["GetAccrualBalancesList"] == null ? null : List.from(json["GetAccrualBalancesList"].map((x) => GetAccrualBalancesList.fromJson(x))); getActionHistoryList = json['GetActionHistoryList']; getAddressDffStructureList = json['GetAddressDffStructureList']; getAddressNotificationBodyList = json['GetAddressNotificationBodyList']; getApprovesList = json['GetApprovesList']; getAttachementList = json['GetAttachementList']; - getAttendanceTrackingList= - json["GetAttendanceTrackingList"] == null ? null : GetAttendanceTracking.fromMap(json["GetAttendanceTrackingList"]); + getAttendanceTrackingList = json["GetAttendanceTrackingList"] == null ? null : GetAttendanceTracking.fromMap(json["GetAttendanceTrackingList"]); getBasicDetColsStructureList = json['GetBasicDetColsStructureList']; getBasicDetDffStructureList = json['GetBasicDetDffStructureList']; getBasicDetNtfBodyList = json['GetBasicDetNtfBodyList']; @@ -611,15 +611,14 @@ class GenericResponseModel { getItemTypeNotificationsList = json['GetItemTypeNotificationsList']; getItemTypesList = json['GetItemTypesList']; getLookupValuesList = json['GetLookupValuesList']; - getMenuEntriesList = json['GetMenuEntriesList']; + getMenuEntriesList= json["GetMenuEntriesList"] == null ? null : List.from(json["GetMenuEntriesList"].map((x) => GetMenuEntriesList.fromJson(x))); getMoItemHistoryList = json['GetMoItemHistoryList']; getMoNotificationBodyList = json['GetMoNotificationBodyList']; getNotificationButtonsList = json['GetNotificationButtonsList']; getNotificationReassignModeList = json['GetNotificationReassignModeList']; getObjectValuesList = json['GetObjectValuesList']; - getOpenMissingSwipesList= json["GetOpenMissingSwipesList"] == null ? null : GetOpenMissingSwipesList.fromJson(json["GetOpenMissingSwipesList"]); - getOpenNotificationsList= - json["GetOpenNotificationsList"] == null ? null : List.from(json["GetOpenNotificationsList"].map((x) => GetOpenNotificationsList.fromMap(x))); + getOpenMissingSwipesList = json["GetOpenMissingSwipesList"] == null ? null : GetOpenMissingSwipesList.fromJson(json["GetOpenMissingSwipesList"]); + getOpenNotificationsList = json["GetOpenNotificationsList"] == null ? null : List.from(json["GetOpenNotificationsList"].map((x) => GetOpenNotificationsList.fromMap(x))); getOpenNotificationsNumList = json['GetOpenNotificationsNumList']; getOpenPeriodDatesList = json['GetOpenPeriodDatesList']; getOrganizationsSalariesList = json['GetOrganizationsSalariesList']; @@ -678,7 +677,7 @@ class GenericResponseModel { listItemImagesDetails = json['List_ItemImagesDetails']; listItemMaster = json['List_ItemMaster']; listMedicineDetails = json['List_MedicineDetails']; - listMenu = json['List_Menu']; + listMenu = json["List_Menu"] == null ? null : List.from(json["List_Menu"].map((x) => ListMenu.fromJson(x))); listNewEmployees = json['List_NewEmployees']; listRadScreen = json['List_RadScreen']; logInTokenID = json['LogInTokenID']; diff --git a/lib/provider/dashboard_provider_model.dart b/lib/provider/dashboard_provider_model.dart index 6ccbfc1..b52448e 100644 --- a/lib/provider/dashboard_provider_model.dart +++ b/lib/provider/dashboard_provider_model.dart @@ -1,23 +1,54 @@ +import 'dart:convert'; + import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:mohem_flutter_app/api/dashboard_api_client.dart'; import 'package:mohem_flutter_app/classes/utils.dart'; import 'package:mohem_flutter_app/main.dart'; import 'package:mohem_flutter_app/models/dashboard/get_attendance_tracking_list_model.dart'; +import 'package:mohem_flutter_app/models/dashboard/itg_forms_model.dart'; +import 'package:mohem_flutter_app/models/dashboard/menu_entries.dart'; +import 'package:mohem_flutter_app/models/dashboard/menus.dart'; +import 'package:mohem_flutter_app/models/generic_response_model.dart'; +import 'package:mohem_flutter_app/widgets/Updater.dart'; /// Mix-in [DiagnosticableTreeMixin] to have access to [debugFillProperties] for the devtool // ignore: prefer_mixin class DashboardProviderModel with ChangeNotifier, DiagnosticableTreeMixin { + //Attendance Tracking bool isAttendanceTrackingLoading = true; - int isTimeRemainingInSeconds = 0; + int endTime = 0, isTimeRemainingInSeconds = 0; GetAttendanceTracking? attendanceTracking; + //Work List + bool isWorkListLoading = true; + int workListCounter = 0; + + //Misssing Swipe + bool isMissingSwipeLoading = true; + int missingSwipeCounter = 0; + + //Leave and Ticket Balance + bool isLeaveTicketBalanceLoading = true; + double leaveBalance = 0; + double ticketBalance = 0; + + //Menu Entries + bool isServicesMenusLoading = true; + List? homeMenus; + List? getMenuEntriesList; + + //Attendance Tracking API's & Methods fetchAttendanceTracking() async { try { attendanceTracking = await DashbaordApiClient().getAttendanceTracking(); isAttendanceTrackingLoading = false; - isTimeRemainingInSeconds = calculateSeconds("00:00:30"); - notifyListeners(); + isTimeRemainingInSeconds = calculateSeconds("00:00:00"); + endTime = DateTime.now().millisecondsSinceEpoch + Duration(seconds: isTimeRemainingInSeconds).inMilliseconds; + print("isTimeRemainingInSeconds " + isTimeRemainingInSeconds.toString()); + print("endTime " + endTime.toString()); + + // notifyListeners(); } catch (ex) { Utils.handleException(ex, null); } @@ -32,8 +63,111 @@ class DashboardProviderModel with ChangeNotifier, DiagnosticableTreeMixin { update() { isAttendanceTrackingLoading = !isAttendanceTrackingLoading; + isWorkListLoading = !isWorkListLoading; attendanceTracking?.pSwipeIn = "a"; - isTimeRemainingInSeconds = calculateSeconds("00:00:30"); + isTimeRemainingInSeconds = calculateSeconds("00:10:30"); + endTime = DateTime.now().millisecondsSinceEpoch + Duration(seconds: isTimeRemainingInSeconds).inMilliseconds; + notifyListeners(); } + + //Work List API's & Methods + fetchWorkListCounter() async { + try { + GenericResponseModel? genericResponseModel = await DashbaordApiClient().getOpenNotifications(); + isWorkListLoading = false; + workListCounter = genericResponseModel?.pOPENNTFNUMBER ?? 0; + ItgFormsModel? itgFormsModel = await DashbaordApiClient().getItgFormsPendingTask(); + workListCounter = workListCounter + (itgFormsModel?.totalCount ?? 0); + notifyListeners(); + } catch (ex) { + isWorkListLoading = false; + logger.wtf(ex); + notifyListeners(); + Utils.handleException(ex, null); + } + } + + //Missing Siwpe API's & Methods + fetchMissingSwipe() async { + try { + GenericResponseModel? genericResponseModel = await DashbaordApiClient().getOpenMissingSwipes(); + isMissingSwipeLoading = false; + missingSwipeCounter = genericResponseModel!.getOpenMissingSwipesList!.pOpenMissingSwipes ?? 0; + notifyListeners(); + } catch (ex) { + isMissingSwipeLoading = false; + logger.wtf(ex); + notifyListeners(); + Utils.handleException(ex, null); + } + } + + //Leave and Ticket Balance API's & Methods + fetchLeaveTicketBalance() async { + try { + GenericResponseModel? genericResponseModel = await DashbaordApiClient().getAccrualBalances(); + isLeaveTicketBalanceLoading = false; + leaveBalance = genericResponseModel?.getAccrualBalancesList![0].accrualNetEntitlement ?? 0.0; + ticketBalance = (genericResponseModel?.getAccrualBalancesList![1].accrualNetEntitlement ?? 0.0) + (genericResponseModel?.getAccrualBalancesList![2].accrualNetEntitlement ?? 0.0); + notifyListeners(); + } catch (ex) { + isLeaveTicketBalanceLoading = false; + logger.wtf(ex); + notifyListeners(); + Utils.handleException(ex, null); + } + } + + //List Menu API's & Methods + fetchListMenu() async { + try { + GenericResponseModel? genericResponseModel = await DashbaordApiClient().getListMenu(); + Map map = {}; + print(jsonEncode(genericResponseModel!.listMenu)); + for (int i = 0; i < genericResponseModel!.listMenu!.length; i++) { + print(genericResponseModel!.listMenu![i]!.menuName ?? ""); + map[genericResponseModel!.listMenu![i]!.menuName ?? ""] = i.toString(); + } + logger.i(map); + notifyListeners(); + } catch (ex) { + logger.wtf(ex); + notifyListeners(); + Utils.handleException(ex, null); + } + } + + //Menu Entries API's & Methods + fetchMenuEntries() async { + try { + GenericResponseModel? genericResponseModel = await DashbaordApiClient().getGetMenuEntries(); + getMenuEntriesList = genericResponseModel!.getMenuEntriesList; + homeMenus = parseMenues(getMenuEntriesList ?? []); + isServicesMenusLoading = false; + notifyListeners(); + } catch (ex) { + logger.wtf(ex); + notifyListeners(); + Utils.handleException(ex, null); + } + } + + List parseMenues(List getMenuEntriesList) { + List menus = []; + for (int i = 0; i < getMenuEntriesList.length; i++) { + if (getMenuEntriesList[i].parentMenuName!.isEmpty) { + menus.add(Menus(getMenuEntriesList[i], getMenuEntriesList.where((element) => getMenuEntriesList[i].menuName == element.parentMenuName).toList())); + } + } + + //Verify Menus by printing in log + // for(int i=0;i { + late var data; + @override void initState() { super.initState(); - final data = Provider.of(context, listen: false); + data = Provider.of(context, listen: false); data.fetchAttendanceTracking(); + data.fetchWorkListCounter(); + data.fetchMissingSwipe(); + data.fetchLeaveTicketBalance(); + // data.fetchMenuEntries(); } - int endTime = 0; - @override void dispose() { super.dispose(); @@ -44,17 +56,8 @@ class _DashboardScreenState extends State { @override Widget build(BuildContext context) { - List names = [LocaleKeys.workList.tr(), LocaleKeys.missingSwipes.tr(), LocaleKeys.leaveBalance.tr(), LocaleKeys.ticketBalance.tr()]; - List namesInt = [118, 02, 18.5, 03]; - List namesColor = [0xff125765, 0xff239D8F, 0xff2BB8A8, 0xff1D92AA]; - - List namesT = [LocaleKeys.monthlyAttendance.tr(), LocaleKeys.workFromHome.tr(), LocaleKeys.ticketRequest.tr(), LocaleKeys.monthlyAttendance.tr()]; - List iconT = ["assets/images/monthly_attendance.svg", "assets/images/work_from_home.svg", "assets/images/ticket_request.svg", "assets/images/work_from_home.svg"]; - List namesD = ["Nostalgia Perfume Perfume", "Al Nafoura", "AlJadi", "Nostalgia Perfume"]; - final data = Provider.of(context); - - endTime=DateTime.now().millisecondsSinceEpoch + Duration(seconds: data.isTimeRemainingInSeconds).inMilliseconds; + return Scaffold( body: Column( children: [ @@ -108,214 +111,134 @@ class _DashboardScreenState extends State { ], ).paddingOnly(left: 21, right: 21, top: 48, bottom: 7), Expanded( - child: ListView( - padding: EdgeInsets.zero, + child: Column( + // padding: EdgeInsets.zero, + // physics: NeverScrollableScrollPhysics(), children: [ - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - LocaleKeys.goodMorning.tr().toText14(color: MyColors.grey77Color), - "Mahmoud Shrouf".toText24(isBold: true), - 16.height, - Row( + Expanded( + child: SingleChildScrollView( + child: Column( children: [ - Expanded( - child: AspectRatio( - aspectRatio: 159 / 159, - child: (data.isAttendanceTrackingLoading - ? GetAttendanceTrackingShimmer() - : Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(15), - gradient: const LinearGradient(transform: GradientRotation(.46), begin: Alignment.topRight, end: Alignment.bottomRight, colors: [ - MyColors.gradiantEndColor, - MyColors.gradiantStartColor, - ]), - ), - child: Stack( - alignment: Alignment.center, - children: [ - if (data.attendanceTracking?.pSwipeIn == null) SvgPicture.asset("assets/images/thumb.svg"), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Expanded( - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - CountdownTimer( - endTime: endTime, - onEnd: null, - endWidget: "00:00:00".toText14(color: Colors.white, isBold: true), - textStyle: TextStyle(color: Colors.white, fontSize: 14, letterSpacing: -0.48, fontWeight: FontWeight.bold), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + LocaleKeys.goodMorning.tr().toText14(color: MyColors.grey77Color), + "Mahmoud Shrouf".toText24(isBold: true), + 16.height, + Row( + children: [ + Expanded( + child: AspectRatio( + aspectRatio: 159 / 159, + child: Consumer( + builder: (context, model, child) { + return (model.isAttendanceTrackingLoading + ? GetAttendanceTrackingShimmer() + : Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(15), + gradient: const LinearGradient(transform: GradientRotation(.46), begin: Alignment.topRight, end: Alignment.bottomRight, colors: [ + MyColors.gradiantEndColor, + MyColors.gradiantStartColor, + ]), ), - LocaleKeys.markAttendance.tr().toText14(color: Colors.white, isBold: true), - if (data.attendanceTracking?.pSwipeIn == null) "01-02-2022".toText12(color: Colors.white), - if (data.attendanceTracking?.pSwipeIn != null) - Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - 9.height, - "07:55:12".toText14(color: Colors.white, isBold: true), - LocaleKeys.timeLeftToday.tr().toText12(color: Colors.white), - 9.height, - const ClipRRect( - borderRadius: BorderRadius.all( - Radius.circular(20), + 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) "01-02-2022".toText12(color: Colors.white), + if (model.isTimeRemainingInSeconds != 0) + Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + 9.height, + CountdownTimer( + endTime: model.endTime, + onEnd: null, + endWidget: "00:00:00".toText14(color: Colors.white, isBold: true), + textStyle: TextStyle(color: Colors.white, fontSize: 14, letterSpacing: -0.48, fontWeight: FontWeight.bold), + ), + LocaleKeys.timeLeftToday.tr().toText12(color: Colors.white), + 9.height, + const ClipRRect( + borderRadius: BorderRadius.all( + Radius.circular(20), + ), + child: LinearProgressIndicator( + value: 0.7, + minHeight: 8, + valueColor: const AlwaysStoppedAnimation(Colors.white), + backgroundColor: const Color(0xff196D73), + ), + ), + ], + ), + ], + ).paddingOnly(top: 12, right: 15, left: 12), ), - child: LinearProgressIndicator( - value: 0.7, - minHeight: 8, - valueColor: const AlwaysStoppedAnimation(Colors.white), - backgroundColor: const Color(0xff196D73), + Row( + children: [ + Expanded( + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + LocaleKeys.checkIn.tr().toText12(color: Colors.white), + (model.isTimeRemainingInSeconds == 0 ? "--:--" : "09:00").toText14(color: Colors.white, isBold: true), + 4.height + ], + ).paddingOnly(left: 12), + ), + Container( + width: 45, + height: 45, + padding: const EdgeInsets.only(left: 14, right: 14), + decoration: const BoxDecoration( + color: Color(0xff259EA4), + borderRadius: BorderRadius.only( + bottomRight: Radius.circular(15), + ), + ), + child: SvgPicture.asset(model.isTimeRemainingInSeconds == 0 ? "assets/images/play.svg" : "assets/images/stop.svg"), + ), + ], ), - ), - ], - ), - ], - ).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), - (data.attendanceTracking?.pSwipeIn == null ? "--:--" : "09:00").toText14(color: Colors.white, isBold: true), - 4.height + ], + ), ], - ).paddingOnly(left: 12), - ), - Container( - width: 45, - height: 45, - padding: const EdgeInsets.only(left: 14, right: 14), - decoration: const BoxDecoration( - color: Color(0xff259EA4), - borderRadius: BorderRadius.only( - bottomRight: Radius.circular(15), - ), ), - child: SvgPicture.asset(data.attendanceTracking?.pSwipeIn == null ? "assets/images/play.svg" : "assets/images/stop.svg"), - ), - ], - ), - ], - ), - ], - ), - ).onPress(() { - Navigator.pushNamed(context, AppRoutes.todayAttendance); - })) - .animatedSwither(), - ), - ), - 9.width, - Expanded( - child: GridView.builder( - shrinkWrap: true, - primary: false, - physics: const NeverScrollableScrollPhysics(), - gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 2, childAspectRatio: 2 / 2, crossAxisSpacing: 9, mainAxisSpacing: 9), - padding: EdgeInsets.zero, - itemCount: 4, - itemBuilder: (BuildContext context, int index) { - return Container( - decoration: BoxDecoration( - color: Color(namesColor[index]), - borderRadius: BorderRadius.circular(10), + ).onPress(() { + Navigator.pushNamed(context, AppRoutes.todayAttendance); + })) + .animatedSwither(); + }, + )), ), - child: Column( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - names[index].toText12(color: Colors.white), - Row( - children: [ - Expanded( - child: namesInt[index].toStringAsFixed(1).toText16(color: Colors.white, isBold: true), - ), - SvgPicture.asset("assets/images/arrow_next.svg", color: Colors.white) - ], - ) - ], - ).paddingOnly(left: 10, right: 10, bottom: 6, top: 6), - ).onPress(() { - Navigator.pushNamed(context, AppRoutes.workList); - }); - }, - ), - ), - ], - ), - 20.height, - Row( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - children: [ - "Other".tr().toText12(), - LocaleKeys.services.tr().toText24(isBold: true), - ], - ), - ), - LocaleKeys.viewAllServices.tr().toText12(isUnderLine: true), - ], - ), - ], - ).paddingOnly(left: 21, right: 21, top: 7), - SizedBox( - height: 105 + 26, - child: ListView.separated( - shrinkWrap: true, - physics: const BouncingScrollPhysics(), - padding: const EdgeInsets.only(left: 21, right: 21, top: 13, bottom: 13), - scrollDirection: Axis.horizontal, - itemBuilder: (cxt, index) { - return AspectRatio( - aspectRatio: 105 / 105, - child: Container( - decoration: BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.circular(15), - boxShadow: [ - BoxShadow( - color: const Color(0xff000000).withOpacity(.05), - blurRadius: 26, - offset: const Offset(0, -3), + 9.width, + Expanded( + child: MenusWidget(), ), ], ), - child: Column( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - SvgPicture.asset(iconT[index]), - Row( - crossAxisAlignment: CrossAxisAlignment.end, - children: [ - Expanded( - child: namesT[index].toText11(isBold: true), - ), - SvgPicture.asset("assets/images/arrow_next.svg").paddingOnly(bottom: 4) - ], - ) - ], - ).paddingOnly(left: 10, right: 10, bottom: 10, top: 12), - ), - ); - }, - separatorBuilder: (cxt, index) => 9.width, - itemCount: 4), + 20.height, + ], + ).paddingOnly(left: 21, right: 21, top: 7), + ServicesWidget(), + 8.height, + ], + ), + ), ), - 8.height, Container( width: double.infinity, padding: EdgeInsets.only(top: 31), diff --git a/lib/ui/landing/widget/menus_widget.dart b/lib/ui/landing/widget/menus_widget.dart new file mode 100644 index 0000000..4d13775 --- /dev/null +++ b/lib/ui/landing/widget/menus_widget.dart @@ -0,0 +1,138 @@ +import 'package:easy_localization/src/public_ext.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:mohem_flutter_app/config/routes.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/provider/dashboard_provider_model.dart'; +import 'package:mohem_flutter_app/widgets/shimmer/dashboard_shimmer_widget.dart'; +import 'package:provider/provider.dart'; + +class MenusWidget extends StatelessWidget { + @override + Widget build(BuildContext context) { + List namesColor = [0xff125765, 0xff239D8F, 0xff2BB8A8, 0xff1D92AA]; + + return Consumer(builder: (context, data, child) { + return GridView( + gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 2, childAspectRatio: 2 / 2, crossAxisSpacing: 9, mainAxisSpacing: 9), + padding: EdgeInsets.zero, + shrinkWrap: true, + primary: false, + physics: const NeverScrollableScrollPhysics(), + children: [ + data.isWorkListLoading + ? MenuShimmer().onPress(() { + data.fetchWorkListCounter(); + }) + : Container( + decoration: BoxDecoration( + color: Color(namesColor[0]), + borderRadius: BorderRadius.circular(10), + ), + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + LocaleKeys.workList.tr().toText12(color: Colors.white), + Row( + children: [ + Expanded( + child: data.workListCounter.toString().toText16(color: Colors.white, isBold: true), + ), + SvgPicture.asset("assets/images/arrow_next.svg", color: Colors.white) + ], + ) + ], + ).paddingOnly(left: 10, right: 10, bottom: 6, top: 6), + ).onPress(() { + // Navigator.pushNamed(context, AppRoutes.workList); + data.fetchWorkListCounter(); + }), + data.isMissingSwipeLoading + ? MenuShimmer().onPress(() { + data.fetchWorkListCounter(); + }) + : Container( + decoration: BoxDecoration( + color: Color(namesColor[1]), + borderRadius: BorderRadius.circular(10), + ), + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + LocaleKeys.missingSwipes.tr().toText12(color: Colors.white), + Row( + children: [ + Expanded( + child: data.missingSwipeCounter.toString().toText16(color: Colors.white, isBold: true), + ), + SvgPicture.asset("assets/images/arrow_next.svg", color: Colors.white) + ], + ) + ], + ).paddingOnly(left: 10, right: 10, bottom: 6, top: 6), + ).onPress(() { + Navigator.pushNamed(context, AppRoutes.workList); + }), + data.isLeaveTicketBalanceLoading + ? MenuShimmer().onPress(() { + data.fetchWorkListCounter(); + }) + : Container( + decoration: BoxDecoration( + color: Color(namesColor[2]), + borderRadius: BorderRadius.circular(10), + ), + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + LocaleKeys.leaveBalance.tr().toText12(color: Colors.white), + Row( + children: [ + Expanded( + child: data.leaveBalance.toString().toText16(color: Colors.white, isBold: true), + ), + SvgPicture.asset("assets/images/arrow_next.svg", color: Colors.white) + ], + ) + ], + ).paddingOnly(left: 10, right: 10, bottom: 6, top: 6), + ).onPress(() { + Navigator.pushNamed(context, AppRoutes.workList); + }), + data.isLeaveTicketBalanceLoading + ? MenuShimmer().onPress(() { + data.fetchWorkListCounter(); + }) + : Container( + decoration: BoxDecoration( + color: Color(namesColor[3]), + borderRadius: BorderRadius.circular(10), + ), + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + LocaleKeys.ticketBalance.tr().toText12(color: Colors.white), + Row( + children: [ + Expanded( + child: data.ticketBalance.toString().toText16(color: Colors.white, isBold: true), + ), + SvgPicture.asset("assets/images/arrow_next.svg", color: Colors.white) + ], + ) + ], + ).paddingOnly(left: 10, right: 10, bottom: 6, top: 6), + ).onPress(() { + Navigator.pushNamed(context, AppRoutes.workList); + }) + ], + ); + }); + } +} diff --git a/lib/ui/landing/widget/services_widget.dart b/lib/ui/landing/widget/services_widget.dart new file mode 100644 index 0000000..f397a4c --- /dev/null +++ b/lib/ui/landing/widget/services_widget.dart @@ -0,0 +1,147 @@ +import 'package:easy_localization/src/public_ext.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_svg/svg.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/provider/dashboard_provider_model.dart'; +import 'package:mohem_flutter_app/widgets/shimmer/dashboard_shimmer_widget.dart'; +import 'package:provider/provider.dart'; + +class ServicesWidget extends StatelessWidget { + @override + Widget build(BuildContext context) { + List namesT = [LocaleKeys.monthlyAttendance.tr(), LocaleKeys.workFromHome.tr(), LocaleKeys.ticketRequest.tr(), LocaleKeys.monthlyAttendance.tr()]; + List iconT = [ + "assets/images/monthly_attendance.svg", + "assets/images/work_from_home.svg", + "assets/images/ticket_request.svg", + "assets/images/work_from_home.svg", + "assets/images/work_from_home.svg", + "assets/images/work_from_home.svg", + "assets/images/work_from_home.svg", + "assets/images/work_from_home.svg" + ]; + + return Consumer( + builder: (context, data, child) { + return data.isServicesMenusLoading + ? whileLoading() + : ListView.separated( + itemBuilder: (context, parentIndex) { + return Column( + children: [ + Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + firstWord(data.homeMenus![parentIndex].menuEntry.prompt!).toText12(), + lastWord(data.homeMenus![parentIndex].menuEntry.prompt!).toText24(isBold: true), + ], + ), + ), + LocaleKeys.viewAllServices.tr().toText12(isUnderLine: true), + ], + ).paddingOnly(left: 21, right: 21), + SizedBox( + height: 105 + 26, + child: ListView.separated( + shrinkWrap: true, + physics: const BouncingScrollPhysics(), + padding: const EdgeInsets.only(left: 21, right: 21, top: 13, bottom: 13), + scrollDirection: Axis.horizontal, + itemBuilder: (cxt, index) { + return AspectRatio( + aspectRatio: 105 / 105, + child: data.isServicesMenusLoading + ? ServicesMenuShimmer() + : Container( + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(15), + boxShadow: [ + BoxShadow( + color: const Color(0xff000000).withOpacity(.05), + blurRadius: 26, + offset: const Offset(0, -3), + ), + ], + ), + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SvgPicture.asset(iconT[index]), + Row( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + Expanded( + child: data.homeMenus![parentIndex].menuEntiesList[index].prompt!.toText11(isBold: true), + ), + SvgPicture.asset("assets/images/arrow_next.svg").paddingOnly(bottom: 4) + ], + ) + ], + ).paddingOnly(left: 10, right: 10, bottom: 10, top: 12), + ), + ); + }, + separatorBuilder: (cxt, index) => 9.width, + itemCount: data.homeMenus![parentIndex].menuEntiesList.length), + ), + ], + ); + }, + separatorBuilder: (context, index) { + return 12.height; + }, + shrinkWrap: true, + physics: NeverScrollableScrollPhysics(), + itemCount: data.homeMenus!.length); + }, + ); + } + + String firstWord(String value) { + return value.split(" ").length > 1 ? value.split(" ")[0] : ""; + } + + String lastWord(String value) { + var parts = value.split(" "); + if (parts.length == 1) { + return value; + } else { + int i = value.indexOf(" "); + return value.substring(i + 1).toCamelCase; + } + } + + Widget whileLoading() { + return Column( + children: [ + ServicesHeaderShimmer().paddingOnly(left: 21, right: 21), + SizedBox( + height: 105 + 26, + child: ListView.separated( + shrinkWrap: true, + physics: const BouncingScrollPhysics(), + padding: const EdgeInsets.only(left: 21, right: 21, top: 13, bottom: 13), + scrollDirection: Axis.horizontal, + itemBuilder: (cxt, index) { + return AspectRatio( + aspectRatio: 105 / 105, + child: ServicesMenuShimmer(), + ); + }, + separatorBuilder: (cxt, index) => 9.width, + itemCount: 4), + ), + ], + ); + } +} diff --git a/lib/widgets/Updater.dart b/lib/widgets/Updater.dart new file mode 100644 index 0000000..154173d --- /dev/null +++ b/lib/widgets/Updater.dart @@ -0,0 +1,39 @@ +/* ZiK */ + +import 'dart:async'; +import 'package:flutter/cupertino.dart'; + +typedef ChildProvider = Widget Function(BuildContext context, E? data); + +class Updater extends StatelessWidget{ + final ChildProvider childProvider; + StreamController? sink; + T? initialData; + List _history = []; + + Stream? _stream; + Updater({T? initialData, required this.childProvider}){ + this.sink = StreamController(); + this.initialData = initialData; + _stream = this.sink?.stream; + } + + @override + Widget build(BuildContext context) { + return StreamBuilder( + initialData: this.initialData, + stream: _stream, + builder: (ctx, snapshot){ + return childProvider(context, snapshot.data); + }); + } + + pushData(T? data) { + _history.add(data); + sink?.sink.add(data); + } + + List getDataHistory() => _history; + T? getLatestData() => _history.last; + +} \ No newline at end of file diff --git a/lib/widgets/shimmer/dashboard_shimmer_widget.dart b/lib/widgets/shimmer/dashboard_shimmer_widget.dart index 1c63d71..5b5bd60 100644 --- a/lib/widgets/shimmer/dashboard_shimmer_widget.dart +++ b/lib/widgets/shimmer/dashboard_shimmer_widget.dart @@ -1,5 +1,6 @@ import 'package:easy_localization/src/public_ext.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_svg/svg.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'; @@ -62,7 +63,9 @@ class GetAttendanceTrackingShimmer extends StatelessWidget { child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, - children: [LocaleKeys.checkIn.tr().toText12(color: Colors.white).toShimmer(),], + children: [ + LocaleKeys.checkIn.tr().toText12(color: Colors.white).toShimmer(), + ], ).paddingOnly(left: 12), ), Container( @@ -80,3 +83,107 @@ class GetAttendanceTrackingShimmer extends StatelessWidget { ); } } + +class MenuShimmer extends StatelessWidget { + @override + Widget build(BuildContext context) { + return Container( + clipBehavior: Clip.antiAlias, + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(15), + boxShadow: [ + BoxShadow( + color: const Color(0xff000000).withOpacity(.05), + blurRadius: 26, + offset: const Offset(0, -3), + ), + ], + ), + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + LocaleKeys.workList.tr().toText12(color: Colors.white).toShimmer(), + Row( + children: [ + Expanded( + flex: 3, + child: 123.toString().toText10(color: Colors.white, isBold: true).toShimmer(), + ), + 12.width, + SvgPicture.asset("assets/images/arrow_next.svg", color: Colors.white).toShimmer() + ], + ) + ], + ).paddingOnly(left: 10, right: 10, bottom: 6, top: 6), + ); + } +} + +class ServicesHeaderShimmer extends StatelessWidget { + @override + Widget build(BuildContext context) { + return Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + "Other".tr().toText10().toShimmer(), + 6.height, + LocaleKeys.services.tr().toText12(isBold: true).toShimmer(), + ], + ), + ), + LocaleKeys.viewAllServices.tr().toText12(isUnderLine: true).toShimmer(), + ], + ); + } +} + +class ServicesMenuShimmer extends StatelessWidget { + @override + Widget build(BuildContext context) { + return Container( + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(15), + boxShadow: [ + BoxShadow( + color: const Color(0xff000000).withOpacity(.05), + blurRadius: 26, + offset: const Offset(0, -3), + ), + ], + ), + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SvgPicture.asset("assets/images/monthly_attendance.svg").toShimmer(), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + "Attendan".toText11(isBold: false).toShimmer(), + 5.height, + Row( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + Expanded( + child: "Attendance".toText11(isBold: false).toShimmer(), + ), + 6.width, + SvgPicture.asset("assets/images/arrow_next.svg").paddingOnly(bottom: 4).toShimmer() + ], + ), + ], + ) + ], + ).paddingOnly(left: 10, right: 10, bottom: 10, top: 12), + ); + } +}