diff --git a/assets/icons/chicken_pattern.svg b/assets/icons/chicken_pattern.svg index d7c33e2..5392fa4 100644 --- a/assets/icons/chicken_pattern.svg +++ b/assets/icons/chicken_pattern.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/assets/images/bg_chicken_pattern.webp b/assets/images/bg_chicken_pattern.webp deleted file mode 100644 index 90eca17..0000000 Binary files a/assets/images/bg_chicken_pattern.webp and /dev/null differ diff --git a/assets/images/live_chicken.jpg b/assets/images/live_chicken.jpg deleted file mode 100644 index d88a231..0000000 Binary files a/assets/images/live_chicken.jpg and /dev/null differ diff --git a/assets/vec/chicken_pattern.svg.vec b/assets/vec/chicken_pattern.svg.vec index a8afc43..45ae488 100644 Binary files a/assets/vec/chicken_pattern.svg.vec and b/assets/vec/chicken_pattern.svg.vec differ diff --git a/packages/chicken/lib/presentation/pages/poultry_science/active_hatching/view.dart b/packages/chicken/lib/presentation/pages/poultry_science/active_hatching/view.dart index 49ebe5a..b415e1e 100644 --- a/packages/chicken/lib/presentation/pages/poultry_science/active_hatching/view.dart +++ b/packages/chicken/lib/presentation/pages/poultry_science/active_hatching/view.dart @@ -1,14 +1,10 @@ import 'package:flutter/material.dart'; -import 'package:get/get.dart'; import 'package:rasadyar_chicken/data/models/response/hatching/hatching_models.dart'; -import 'package:rasadyar_chicken/data/models/response/hatching_report/hatching_report.dart'; import 'package:rasadyar_chicken/presentation/pages/poultry_science/active_hatching/logic.dart'; import 'package:rasadyar_chicken/presentation/utils/nested_keys_utils.dart'; import 'package:rasadyar_chicken/presentation/widget/base_page/view.dart'; import 'package:rasadyar_chicken/presentation/widget/page_route.dart'; import 'package:rasadyar_core/core.dart'; -import 'package:rasadyar_core/presentation/widget/list_item/list_item2.dart'; -import 'package:rasadyar_core/presentation/widget/list_view/r_paginated_list_view.dart'; class ActiveHatchingPage extends GetView { const ActiveHatchingPage({super.key}); @@ -18,11 +14,22 @@ class ActiveHatchingPage extends GetView { return BasePage( hasSearch: false, hasFilter: false, - routesWidget: buildContainerPageRoute(controller.routesName), + routesWidget: buildContainerPageRoute(controller.routesName), onBackPressed: () => Get.back(id: poultryFirstKey), - widgets: [ + child: Stack( + children: [ + Positioned.fill( + child: Assets.vec.chickenPatternSvg.svg( + fit: BoxFit.cover, + colorFilter: ColorFilter.mode(Colors.grey.shade400, BlendMode.srcIn), + ), + ), + Positioned.fill(child: Column(children: [hatchingWidget()])), + ], + ), + /*widgets: [ hatchingWidget() - ], + ],*/ ); } @@ -99,8 +106,10 @@ class ActiveHatchingPage extends GetView { child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - - Text('نژاد:${item.breed?.first.breed ?? 'N/A'}', style: AppFonts.yekan14.copyWith(color: AppColor.textColor)), + Text( + 'نژاد:${item.breed?.first.breed ?? 'N/A'}', + style: AppFonts.yekan14.copyWith(color: AppColor.textColor), + ), Text( ' سن${item.age} (روز)', @@ -120,8 +129,12 @@ class ActiveHatchingPage extends GetView { value: item.quantity.separatedByCommaFa, unit: '(قطعه)', ), - buildUnitRow(title: 'مانده در سالن', value: item.leftOver.separatedByCommaFa,unit: '(قطعه)',), - buildUnitRow(title: 'تلفات', value: item.losses.separatedByCommaFa,unit: '(قطعه)',), + buildUnitRow( + title: 'مانده در سالن', + value: item.leftOver.separatedByCommaFa, + unit: '(قطعه)', + ), + buildUnitRow(title: 'تلفات', value: item.losses.separatedByCommaFa, unit: '(قطعه)'), buildRow( title: 'دامپزشک فارم', value: '${item.vetFarm?.vetFarmFullName}(${item.vetFarm?.vetFarmMobile})', @@ -136,7 +149,6 @@ class ActiveHatchingPage extends GetView { color: (item.reportInfo?.image ?? false) ? AppColor.greenNormal : AppColor.redDark, ), ), - ], ), ); diff --git a/packages/chicken/lib/presentation/pages/poultry_science/farm/view.dart b/packages/chicken/lib/presentation/pages/poultry_science/farm/view.dart index 007ed51..1a8d05a 100644 --- a/packages/chicken/lib/presentation/pages/poultry_science/farm/view.dart +++ b/packages/chicken/lib/presentation/pages/poultry_science/farm/view.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:rasadyar_chicken/data/models/response/poultry_farm/poultry_farm.dart'; - import 'package:rasadyar_chicken/presentation/utils/nested_keys_utils.dart'; +import 'package:rasadyar_core/presentation/widget/base_page/widgets/back_ground_widget.dart'; import 'package:rasadyar_chicken/presentation/widget/base_page/view.dart'; import 'package:rasadyar_chicken/presentation/widget/page_route.dart'; import 'package:rasadyar_core/core.dart'; @@ -16,9 +16,15 @@ class FarmPage extends GetView { return BasePage( hasFilter: false, hasSearch: false, - routesWidget: buildContainerPageRoute(controller.routesName), + isBase: true, + routes: [], onBackPressed: () => Get.back(id: poultryFirstKey), - widgets: [firstTagInformation(), farmListWidget()], + child: Stack( + children: [ + Positioned.fill(child: chickenBackground()), + Positioned.fill(child: Column(children: [firstTagInformation(), farmListWidget()])), + ], + ), ); } @@ -134,7 +140,11 @@ class FarmPage extends GetView { title: 'دامپزشک فارم', value: '${item.vetFarm?.fullName} (${item.vetFarm?.mobile ?? '-'})', ), - buildUnitRow(title: 'ظرفیت فارم', value: item.totalCapacity.separatedByCommaFa, unit: '(قطعه)'), + buildUnitRow( + title: 'ظرفیت فارم', + value: item.totalCapacity.separatedByCommaFa, + unit: '(قطعه)', + ), buildRow( title: 'جوجه ریزی فعال (تعداد دوره) ', value: diff --git a/packages/chicken/lib/presentation/pages/poultry_science/home/view.dart b/packages/chicken/lib/presentation/pages/poultry_science/home/view.dart index d77aed0..2d5adcc 100644 --- a/packages/chicken/lib/presentation/pages/poultry_science/home/view.dart +++ b/packages/chicken/lib/presentation/pages/poultry_science/home/view.dart @@ -19,13 +19,7 @@ class PoultryScienceHomePage extends GetView { hasBack: false, hasFilter: false, hasSearch: false, - additionalActions: [ - SizedBox(width: 12), - Icon(CupertinoIcons.envelope_fill, color: Colors.white), - SizedBox(width: 12), - Icon(CupertinoIcons.bell_fill, color: Colors.white), - SizedBox(width: 12), - ], + ), body: SingleChildScrollView( physics: BouncingScrollPhysics(), diff --git a/packages/chicken/lib/presentation/pages/poultry_science/inspection/view.dart b/packages/chicken/lib/presentation/pages/poultry_science/inspection/view.dart index 4ff0916..bb25a47 100644 --- a/packages/chicken/lib/presentation/pages/poultry_science/inspection/view.dart +++ b/packages/chicken/lib/presentation/pages/poultry_science/inspection/view.dart @@ -15,22 +15,32 @@ class InspectionPoultrySciencePage extends GetView controller.setSearchValue(data), + backId: poultryFirstKey, + routes: controller.routesName, + //routesWidget: ObxValue((route) => buildContainerPageRoute(route), controller.routesName), + child: Stack( + children: [ + Positioned.fill( + child: Assets.vec.chickenPatternSvg.svg( + fit: BoxFit.cover, + colorFilter: ColorFilter.mode(Color(0xFFC3CBDA), BlendMode.srcIn), + ), + ), + SizedBox(height: 50, child: segmentWidget()), - routesWidget: ObxValue((route) => buildContainerPageRoute(route), controller.routesName), - onBackPressed: () => Get.back(id: poultryFirstKey), - widgets: [ - segmentWidget(), - - ObxValue((data) { - return data.value == 0 ? hatchingWidget() : reportWidget(); - }, controller.selectedSegmentIndex), - ], + Expanded( + child: ObxValue((data) { + return data.value == 0 ? hatchingWidget() : reportWidget(); + }, controller.selectedSegmentIndex), + ), + ], + ), ); } @@ -109,9 +119,10 @@ class InspectionPoultrySciencePage extends GetView - Container( - height: 100.h, - clipBehavior: Clip.hardEdge, - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(8.r), - image: DecorationImage( - image: NetworkImage(item.image?[index] ?? ''), - fit: BoxFit.cover, - ), - ), + itemBuilder: (context, index) => Container( + height: 100.h, + clipBehavior: Clip.hardEdge, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(8.r), + image: DecorationImage( + image: NetworkImage(item.image?[index] ?? ''), + fit: BoxFit.cover, ), + ), + ), ), ), ], @@ -639,7 +654,6 @@ class InspectionPoultrySciencePage extends GetView ProfileLogic(), fenix: true); - Get.lazyPut(() => SearchLogic(), fenix: true); + //root logics } diff --git a/packages/chicken/lib/presentation/routes/pages.dart b/packages/chicken/lib/presentation/routes/pages.dart index 88af388..446ad3f 100644 --- a/packages/chicken/lib/presentation/routes/pages.dart +++ b/packages/chicken/lib/presentation/routes/pages.dart @@ -1,7 +1,5 @@ import 'package:rasadyar_chicken/presentation/pages/common/auth/logic.dart'; import 'package:rasadyar_chicken/presentation/pages/common/auth/view.dart'; -import 'package:rasadyar_chicken/presentation/pages/common/profile/logic.dart'; -import 'package:rasadyar_chicken/presentation/pages/common/profile/view.dart'; import 'package:rasadyar_chicken/presentation/pages/common/role/logic.dart'; import 'package:rasadyar_chicken/presentation/pages/common/role/view.dart'; import 'package:rasadyar_chicken/presentation/pages/poultry_science/active_hatching/logic.dart'; @@ -22,9 +20,7 @@ import 'package:rasadyar_chicken/presentation/pages/poultry_science/root/view.da import 'package:rasadyar_chicken/presentation/pages/steward/steward.dart'; import 'package:rasadyar_chicken/presentation/routes/global_binding.dart'; import 'package:rasadyar_chicken/presentation/routes/routes.dart'; -import 'package:rasadyar_chicken/presentation/widget/base_page/logic.dart'; import 'package:rasadyar_chicken/presentation/widget/captcha/logic.dart'; -import 'package:rasadyar_chicken/presentation/widget/search/logic.dart'; import 'package:rasadyar_core/core.dart'; sealed class ChickenPages { @@ -93,7 +89,6 @@ sealed class ChickenPages { page: () => SalesOutOfProvincePage(), middlewares: [AuthMiddleware()], binding: BindingsBuilder(() { - Get.lazyPut(() => SearchLogic()); Get.lazyPut(() => SalesOutOfProvinceLogic()); Get.lazyPut(() => SalesOutOfProvinceBuyersLogic()); Get.lazyPut(() => SalesOutOfProvinceSalesListLogic()); @@ -104,7 +99,6 @@ sealed class ChickenPages { page: () => SalesOutOfProvinceBuyersPage(), middlewares: [AuthMiddleware()], binding: BindingsBuilder(() { - Get.lazyPut(() => SearchLogic()); Get.lazyPut(() => SalesOutOfProvinceLogic()); Get.lazyPut(() => SalesOutOfProvinceBuyersLogic()); Get.lazyPut(() => SalesOutOfProvinceSalesListLogic()); @@ -117,7 +111,6 @@ sealed class ChickenPages { binding: BindingsBuilder(() { Get.lazyPut(() => BaseLogic()); Get.lazyPut(() => SalesInProvinceLogic()); - Get.lazyPut(() => SearchLogic()); }), ), @@ -137,7 +130,6 @@ sealed class ChickenPages { middlewares: [AuthMiddleware()], binding: BindingsBuilder(() { Get.lazyPut(() => BaseLogic()); - Get.lazyPut(() => SearchLogic()); Get.lazyPut(() => BuyOutOfProvinceLogic()); }), ), @@ -147,7 +139,6 @@ sealed class ChickenPages { middlewares: [AuthMiddleware()], binding: BindingsBuilder(() { Get.lazyPut(() => BaseLogic()); - Get.lazyPut(() => SearchLogic()); Get.lazyPut(() => BuyInProvinceLogic()); Get.lazyPut(() => BuyInProvinceWaitingLogic()); Get.lazyPut(() => BuyInProvinceAllLogic()); @@ -178,7 +169,7 @@ sealed class ChickenPages { middlewares: [AuthMiddleware()], binding: BindingsBuilder(() { Get.lazyPut(() => InspectionPoultryScienceLogic()); - Get.lazyPut(() => SearchLogic()); + }), ), GetPage( diff --git a/packages/chicken/lib/presentation/widget/app_bar.dart b/packages/chicken/lib/presentation/widget/app_bar.dart index 05a06fe..246fc43 100644 --- a/packages/chicken/lib/presentation/widget/app_bar.dart +++ b/packages/chicken/lib/presentation/widget/app_bar.dart @@ -1,99 +1,37 @@ import 'package:flutter/material.dart'; -import 'package:rasadyar_chicken/presentation/widget/base_page/logic.dart'; -import 'package:rasadyar_chicken/presentation/widget/search/logic.dart'; import 'package:rasadyar_core/core.dart'; +/// Creates a customized AppBar for the Rasadyar Chicken app. RAppBar chickenAppBar({ bool hasBack = true, bool hasFilter = true, bool hasSearch = true, bool isBase = false, - VoidCallback? onBackPressed, - GestureTapCallback? onFilterTap, - GestureTapCallback? onSearchTap, - List? additionalActions, + bool hasNotification = false, + bool hasNews = false, + int? backId, + VoidCallback? onBackTap, + VoidCallback? onFilterTap, + VoidCallback? onSearchTap, + VoidCallback? onNewsTap, + VoidCallback? onNotificationTap, }) { return RAppBar( - hasBack: isBase == true ? false : hasBack, - onBackPressed: onBackPressed, - leadingWidth: 155, - leading: Row( - mainAxisSize: MainAxisSize.min, - spacing: 6, - children: [ - Text('رصدطیور', style: AppFonts.yekan16Bold.copyWith(color: Colors.white)), - Assets.vec.chickenSvg.svg( - width: 24, - height: 24, - ), - ], - ), - additionalActions: [ - if (!isBase && hasSearch) searchWidget(onSearchTap), - SizedBox(width: 8), - if (!isBase && hasFilter) filterWidget(onFilterTap), - SizedBox(width: 8), - if (additionalActions != null) ...additionalActions, + hasBack: hasBack, + hasSearch: hasSearch, + hasNotification: hasNotification, + hasNews: hasNews, + isBase: isBase, + backId: backId, + onSearchTap: onSearchTap, + onBackTap: onBackTap, + onNotificationTap: onNotificationTap, + onNewsTap: onNewsTap, + backgroundColor: AppColor.blueNormal, + children: [ + Text('رصدطیور', style: AppFonts.yekan16Bold), + const SizedBox(width: 6), + Assets.vec.chickenSvg.svg(height: 24, width: 24), ], ); } - - -GestureDetector filterWidget(GestureTapCallback? onFilterTap) { - return GestureDetector( - onTap: onFilterTap, - child: Stack( - alignment: Alignment.topRight, - children: [ - Assets.vec.filterOutlineSvg.svg( - width: 20, - height: 20, - colorFilter: const ColorFilter.mode(Colors.white, BlendMode.srcIn), - ), - Obx(() { - final controller = Get.find(); - return Visibility( - visible: controller.isFilterSelected.value, - child: Container( - width: 8, - height: 8, - decoration: const BoxDecoration( - color: Colors.red, - shape: BoxShape.circle, - ), - ), - ); - }), - ], - ), - ); -} -GestureDetector searchWidget(GestureTapCallback? onSearchTap) { - return GestureDetector( - onTap: onSearchTap, - child: Stack( - alignment: Alignment.topRight, - children: [ - Assets.vec.searchSvg.svg( - width: 24, - height: 24, - colorFilter: const ColorFilter.mode(Colors.white, BlendMode.srcIn), - ), - Obx(() { - final controller = Get.find(); - return Visibility( - visible: controller.searchValue.value!=null, - child: Container( - width: 8, - height: 8, - decoration: const BoxDecoration( - color: Colors.red, - shape: BoxShape.circle, - ), - ), - ); - }), - ], - ), - ); -} \ No newline at end of file diff --git a/packages/chicken/lib/presentation/widget/base_page/logic.dart b/packages/chicken/lib/presentation/widget/base_page/logic.dart deleted file mode 100644 index f33fa71..0000000 --- a/packages/chicken/lib/presentation/widget/base_page/logic.dart +++ /dev/null @@ -1,9 +0,0 @@ -import 'package:rasadyar_core/core.dart'; - -class BaseLogic extends GetxController { - final RxBool isFilterSelected = false.obs; - - void toggleFilter() { - isFilterSelected.value = !isFilterSelected.value; - } -} diff --git a/packages/chicken/lib/presentation/widget/base_page/view.dart b/packages/chicken/lib/presentation/widget/base_page/view.dart index cb56875..8f2b8bf 100644 --- a/packages/chicken/lib/presentation/widget/base_page/view.dart +++ b/packages/chicken/lib/presentation/widget/base_page/view.dart @@ -1,112 +1,70 @@ import 'package:flutter/material.dart'; import 'package:rasadyar_chicken/presentation/widget/app_bar.dart'; -import 'package:rasadyar_chicken/presentation/widget/base_page/logic.dart'; -import 'package:rasadyar_chicken/presentation/widget/page_route.dart'; -import 'package:rasadyar_chicken/presentation/widget/search/logic.dart'; -import 'package:rasadyar_chicken/presentation/widget/search/view.dart'; import 'package:rasadyar_core/core.dart'; -class BasePage extends StatefulWidget { - const BasePage({ +class ChickenBasePage extends GetView { + const ChickenBasePage({ super.key, - this.routes, - this.widgets, - this.routesWidget, - this.floatingActionButtonLocation, - this.floatingActionButton, - this.onSearchChanged, - this.child, this.hasBack = true, this.hasFilter = true, this.hasSearch = true, this.isBase = false, - this.onBackPressed, + this.hasNotification = false, + this.hasNews = false, + this.backId, + this.onBackTap, this.onFilterTap, this.onSearchTap, + this.onNewsTap, + this.onNotificationTap, + this.onSearchChanged, + this.routes, + this.routesWidget, + this.widgets, + this.child, + this.scrollable = false, + this.floatingActionButtonLocation, + this.floatingActionButton, + this.filteringWidget, }) : assert( (routes != null) || routesWidget != null, 'Either routes or routesWidget must be provided.', ); + //AppBar properties` + final bool hasBack; + final bool hasFilter; + final bool hasSearch; + final bool isBase; + final bool hasNotification; + final bool hasNews; + final int? backId; + + final VoidCallback? onBackTap; + final VoidCallback? onFilterTap; + final VoidCallback? onSearchTap; + final VoidCallback? onNewsTap; + final VoidCallback? onNotificationTap; + final List? routes; - final Widget? routesWidget; + final Breadcrumb? routesWidget; final List? widgets; final Widget? child; + final bool scrollable; + final FloatingActionButtonLocation? floatingActionButtonLocation; final Widget? floatingActionButton; final Widget? filteringWidget; final void Function(String?)? onSearchChanged; - final bool hasBack; - final bool hasFilter; - final bool hasSearch; - final bool isBase; - final VoidCallback? onBackPressed; - final GestureTapCallback? onFilterTap; - final GestureTapCallback? onSearchTap; - - @override - State createState() => _BasePageState(); -} - -class _BasePageState extends State { - BaseLogic get controller => Get.find(); - Worker? filterWorker; - bool _isBottomSheetOpen = false; - - @override - void initState() { - super.initState(); - /* filterWorker = ever(controller.isFilterSelected, (bool isSelected) { - if (!mounted) return; - - if (isSelected && widget.filteringWidget != null) { - // بررسی اینکه آیا bottomSheet از قبل باز است یا نه - if (_isBottomSheetOpen) { - controller.isFilterSelected.value = false; - return; - } - - // بررسی اینکه آیا route فعلی current است یا نه - if (ModalRoute.of(context)?.isCurrent != true) { - controller.isFilterSelected.value = false; - return; - } - - _isBottomSheetOpen = true; - Get.bottomSheet( - widget.filteringWidget!, - isScrollControlled: true, - isDismissible: true, - enableDrag: true, - ).then((_) { - // تنظیم مقدار به false بعد از بسته شدن bottomSheet - if (mounted) { - _isBottomSheetOpen = false; - controller.isFilterSelected.value = false; - } - }); - } - });*/ - } - - @override - void dispose() { - filterWorker?.dispose(); - super.dispose(); - } void _onFilterTap() { - if (widget.hasFilter && widget.filteringWidget != null) { - // بررسی اینکه آیا این route در top است یا نه - final currentRoute = ModalRoute.of(context); - if (currentRoute?.isCurrent != true) { - return; - } + if (hasFilter && filteringWidget != null) { + final currentRoute = ModalRoute.of(Get.context!); + if (currentRoute?.isCurrent != true) return; - // مستقیماً bottomSheet را باز کنید Get.bottomSheet( - widget.filteringWidget!, + filteringWidget!, isScrollControlled: true, isDismissible: true, enableDrag: true, @@ -116,140 +74,27 @@ class _BasePageState extends State { @override Widget build(BuildContext context) { - return PopScope( - canPop: false, - onPopInvokedWithResult: (didPop, result) => widget.onBackPressed, - child: Scaffold( - backgroundColor: AppColor.bgLight, - appBar: chickenAppBar( - hasBack: widget.isBase ? false : widget.hasBack, - onBackPressed: widget.onBackPressed, - hasFilter: widget.hasFilter, - hasSearch: widget.hasSearch, - isBase: widget.isBase, - onFilterTap: widget.hasFilter ? _onFilterTap : null, - onSearchTap: widget.hasSearch ? () => Get.find().toggleSearch() : null, - ), - body: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - widget.routesWidget != null ? widget.routesWidget! : buildPageRoute(widget.routes!), - if (!widget.isBase && widget.hasSearch) ...{ - SearchWidget(onSearchChanged: widget.onSearchChanged), - }, - - if (widget.child != null) ...{Expanded(child: widget.child!)}, - ...?widget.widgets, - ], - ), - floatingActionButtonLocation: widget.floatingActionButtonLocation, - floatingActionButton: widget.floatingActionButton, - ), - ); - } -} - -class BasePageWithScroll extends StatefulWidget { - const BasePageWithScroll({ - super.key, - this.routes, - required this.widgets, - this.routesWidget, - this.floatingActionButtonLocation, - this.floatingActionButton, - this.onSearchChanged, - this.hasBack = true, - this.hasFilter = true, - this.hasSearch = true, - this.isBase = false, - this.onBackPressed, - this.onFilterTap, - this.onSearchTap, - this.filteringWidget, - }) : assert( - (routes != null) || routesWidget != null, - 'Either routes or routesWidget must be provided.', - ); - - final List? routes; - final Widget? routesWidget; - final List widgets; - final FloatingActionButtonLocation? floatingActionButtonLocation; - final Widget? floatingActionButton; - final Widget? filteringWidget; - final void Function(String?)? onSearchChanged; - final bool hasBack; - final bool hasFilter; - final bool hasSearch; - final bool isBase; - final VoidCallback? onBackPressed; - final GestureTapCallback? onFilterTap; - final GestureTapCallback? onSearchTap; - - @override - State createState() => _BasePageWithScrollState(); -} - -class _BasePageWithScrollState extends State { - BaseLogic get controller => Get.find(); - Worker? filterWorker; - - @override - void dispose() { - filterWorker?.dispose(); - super.dispose(); - } - - void _onFilterTap() { - if (widget.hasFilter && widget.filteringWidget != null) { - // بررسی اینکه آیا این route در top است یا نه - final currentRoute = ModalRoute.of(context); - if (currentRoute?.isCurrent != true) { - return; - } - - // مستقیماً bottomSheet را باز کنید - Get.bottomSheet( - widget.filteringWidget!, - isScrollControlled: true, - isDismissible: true, - enableDrag: true, - ); - } - } - - @override - Widget build(BuildContext context) { - return PopScope( - canPop: false, - onPopInvokedWithResult: (didPop, result) => widget.onBackPressed, - child: Scaffold( - backgroundColor: AppColor.bgLight, - appBar: chickenAppBar( - hasBack: widget.isBase ? false : widget.hasBack, - onBackPressed: widget.onBackPressed, - hasFilter: widget.hasFilter, - hasSearch: widget.hasSearch, - isBase: widget.isBase, - onFilterTap: widget.hasFilter ? _onFilterTap : null, - onSearchTap: widget.hasSearch ? () => Get.find().toggleSearch() : null, - ), - body: SingleChildScrollView( - physics: BouncingScrollPhysics(), - padding: EdgeInsets.symmetric(vertical: 8.h), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - widget.routesWidget != null ? widget.routesWidget! : buildPageRoute(widget.routes!), - if (!widget.isBase && widget.hasSearch) ...{ - SearchWidget(onSearchChanged: widget.onSearchChanged), - }, - ...widget.widgets, - ], - ), - ), - floatingActionButtonLocation: widget.floatingActionButtonLocation, - floatingActionButton: widget.floatingActionButton, + return BasePage( + routes: routes, + routesWidget: routesWidget, + widgets: widgets, + child: child, + scrollable: scrollable, + floatingActionButtonLocation: floatingActionButtonLocation, + floatingActionButton: floatingActionButton, + appBar: chickenAppBar( + isBase: isBase, + hasBack: isBase ? false : hasBack, + onBackTap: onBackTap, + onNewsTap: onNewsTap, + hasFilter: hasFilter, + hasSearch: hasSearch, + hasNews: hasNews, + hasNotification: hasNotification, + backId: backId, + onNotificationTap: onNotificationTap, + onFilterTap: hasFilter ? _onFilterTap : null, + onSearchTap: hasSearch ? controller.toggleSearch : null, ), ); } diff --git a/packages/chicken/lib/presentation/widget/page_route.dart b/packages/chicken/lib/presentation/widget/page_route.dart index eeaaf25..7e0248e 100644 --- a/packages/chicken/lib/presentation/widget/page_route.dart +++ b/packages/chicken/lib/presentation/widget/page_route.dart @@ -2,10 +2,13 @@ import 'package:flutter/cupertino.dart'; import 'package:rasadyar_core/core.dart'; Widget buildPageRoute(List route) { + if(route.isEmpty){ + return SizedBox.shrink(); + } return Padding( padding: const EdgeInsets.fromLTRB(0, 4, 7, 4), child: Text( - route.isEmpty ? 'خانه' : route.join(" > "), + route.join(" > "), style: AppFonts.yekan14.copyWith(color: AppColor.bgDark), ), ); diff --git a/packages/chicken/lib/presentation/widget/search/logic.dart b/packages/chicken/lib/presentation/widget/search/logic.dart deleted file mode 100644 index 4379a4a..0000000 --- a/packages/chicken/lib/presentation/widget/search/logic.dart +++ /dev/null @@ -1,21 +0,0 @@ -import 'package:rasadyar_core/core.dart'; - -class SearchLogic extends GetxController { - final RxBool isSearchSelected = false.obs; - - final RxnString searchValue = RxnString(); - - void setSearchCallback(void Function(String)? onSearchChanged) { - debounce(searchValue, (val) { - if (val != null && val.trim().isNotEmpty) { - onSearchChanged?.call(val); - } - }, time: const Duration(milliseconds: 600)); - } - - void toggleSearch() { - isSearchSelected.value = !isSearchSelected.value; - } - - -} diff --git a/packages/core/lib/presentation/common/assets.gen.dart b/packages/core/lib/presentation/common/assets.gen.dart index 512eff0..93f3921 100644 --- a/packages/core/lib/presentation/common/assets.gen.dart +++ b/packages/core/lib/presentation/common/assets.gen.dart @@ -460,18 +460,12 @@ class $AssetsIconsGen { class $AssetsImagesGen { const $AssetsImagesGen(); - /// File path: assets/images/bg_chicken_pattern.webp - AssetGenImage get bgChickenPattern => const AssetGenImage('assets/images/bg_chicken_pattern.webp'); - /// File path: assets/images/chicken.png AssetGenImage get chicken => const AssetGenImage('assets/images/chicken.png'); /// File path: assets/images/inner_splash.webp AssetGenImage get innerSplash => const AssetGenImage('assets/images/inner_splash.webp'); - /// File path: assets/images/live_chicken.jpg - AssetGenImage get liveChicken => const AssetGenImage('assets/images/live_chicken.jpg'); - /// File path: assets/images/outter_splash.webp AssetGenImage get outterSplash => const AssetGenImage('assets/images/outter_splash.webp'); @@ -482,15 +476,7 @@ class $AssetsImagesGen { AssetGenImage get selectRole => const AssetGenImage('assets/images/select_role.webp'); /// List of all assets - List get values => [ - bgChickenPattern, - chicken, - innerSplash, - liveChicken, - outterSplash, - placeHolder, - selectRole, - ]; + List get values => [chicken, innerSplash, outterSplash, placeHolder, selectRole]; } class $AssetsLogosGen { diff --git a/packages/core/lib/presentation/widget/base_page/logic.dart b/packages/core/lib/presentation/widget/base_page/logic.dart new file mode 100644 index 0000000..bccafe7 --- /dev/null +++ b/packages/core/lib/presentation/widget/base_page/logic.dart @@ -0,0 +1,31 @@ +import 'package:flutter/material.dart'; +import 'package:rasadyar_core/core.dart'; + +class BaseLogic extends GetxController { + final RxBool isFilterSelected = false.obs; + final RxBool isSearchSelected = false.obs; + final RxnString searchValue = RxnString(); + final TextEditingController textEditingController = TextEditingController(); + + void setSearchCallback(void Function(String?)? onSearchChanged) { + debounce(searchValue, (val) { + if (val != null && val.trim().isNotEmpty) { + onSearchChanged?.call(val); + } + }, time: const Duration(milliseconds: 600)); + } + + void toggleSearch() { + isSearchSelected.value = !isSearchSelected.value; + } + + void clearSearch() { + textEditingController.clear(); + searchValue.value = null; + isSearchSelected.value = false; + } + + void toggleFilter() { + isFilterSelected.value = !isFilterSelected.value; + } +} diff --git a/packages/core/lib/presentation/widget/base_page/view.dart b/packages/core/lib/presentation/widget/base_page/view.dart new file mode 100644 index 0000000..7fdeb4e --- /dev/null +++ b/packages/core/lib/presentation/widget/base_page/view.dart @@ -0,0 +1,91 @@ +import 'package:flutter/material.dart'; +import 'package:rasadyar_core/core.dart'; + +class BasePage extends GetView { + const BasePage({ + super.key, + this.routes, + this.routesWidget, + this.widgets, + this.child, + this.scrollable = false, + this.floatingActionButtonLocation, + this.floatingActionButton, + this.appBar, + this.backGroundWidget, + }) : assert( + (routes != null) || routesWidget != null, + 'Either routes or routesWidget must be provided.', + ); + + final List? routes; + final Breadcrumb? routesWidget; + final List? widgets; + final Widget? child; + final bool scrollable; + final RAppBar? appBar; + final BackGroundWidget? backGroundWidget; + final FloatingActionButtonLocation? floatingActionButtonLocation; + final Widget? floatingActionButton; + + Widget _buildHeader() { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + routesWidget ?? TextBreadcrumb(routes: routes!), + if (controller.isSearchSelected.value) ...{SearchWidget()}, + ], + ); + } + + Widget _buildBody() { + final content = [_buildHeader(), if (child != null) Expanded(child: child!), ...?widgets]; + + if (scrollable) { + if (backGroundWidget != null) { + return Stack( + children: [ + ?backGroundWidget, + SingleChildScrollView( + physics: const BouncingScrollPhysics(), + padding: EdgeInsets.symmetric(vertical: 8.h), + child: Column(children: content), + ), + ], + ); + } + return SingleChildScrollView( + physics: const BouncingScrollPhysics(), + padding: EdgeInsets.symmetric(vertical: 8.h), + child: Column(children: content), + ); + } + + if (backGroundWidget != null) { + return Stack( + children: [ + ?backGroundWidget, + Column(children: content), + ], + ); + } + return Column(children: content); + } + + @override + Widget build(BuildContext context) { + return PopScope( + canPop: false, + onPopInvokedWithResult: (didPop, result) { + if (!didPop) appBar?.onBackTap?.call(); + }, + child: Scaffold( + backgroundColor: AppColor.bgLight, + appBar: appBar, + body: _buildBody(), + floatingActionButtonLocation: floatingActionButtonLocation, + floatingActionButton: floatingActionButton, + ), + ); + } +} diff --git a/packages/core/lib/presentation/widget/base_page/widgets/back_ground_widget.dart b/packages/core/lib/presentation/widget/base_page/widgets/back_ground_widget.dart new file mode 100644 index 0000000..0ab96ab --- /dev/null +++ b/packages/core/lib/presentation/widget/base_page/widgets/back_ground_widget.dart @@ -0,0 +1,36 @@ +import 'package:flutter/material.dart'; +import 'package:rasadyar_core/presentation/common/assets.gen.dart'; + +class BackGroundWidget extends StatelessWidget { + const BackGroundWidget({super.key, required this.gradient, required this.vecPath}); + + final Gradient gradient; + final String vecPath; + + @override + Widget build(BuildContext context) { + return Container( + decoration: BoxDecoration(gradient: gradient), + child: SvgGenImage.vec(vecPath).svg(fit: BoxFit.cover), + ); + } +} + +/*Container chickenBackground() { + return Container( + decoration: BoxDecoration( + gradient: + gradient ?? + LinearGradient( + begin: Alignment(1.00, 0.01), + end: Alignment(0.04, 0.99), + colors: [ + const Color(0xFFD6DCEF).withValues(alpha: .8), + const Color(0xFFD6E6E9).withValues(alpha: .8), + const Color(0xFFD6E4E3).withValues(alpha: .8), + ], + ), + ), + child: Assets.vec.chickenPatternSvg.svg(fit: BoxFit.cover), + ); +}*/ diff --git a/packages/core/lib/presentation/widget/base_page/widgets/breadcrumb.dart b/packages/core/lib/presentation/widget/base_page/widgets/breadcrumb.dart new file mode 100644 index 0000000..e5f93a1 --- /dev/null +++ b/packages/core/lib/presentation/widget/base_page/widgets/breadcrumb.dart @@ -0,0 +1,58 @@ +import 'package:flutter/material.dart'; +import 'package:rasadyar_core/core.dart'; + +abstract class Breadcrumb extends StatelessWidget { + const Breadcrumb({super.key, this.routes}); + + final List? routes; +} + +class TextBreadcrumb extends Breadcrumb { + const TextBreadcrumb({super.key, super.routes}); + + @override + Widget build(BuildContext context) { + if (routes?.isEmpty ?? true) { + return SizedBox.shrink(); + } + return Padding( + padding: const EdgeInsets.fromLTRB(0, 4, 7, 4), + child: Text(routes!.join(" > "), style: AppFonts.yekan14.copyWith(color: AppColor.bgDark)), + ); + } +} + + +class ContainerBreadcrumb extends Breadcrumb { + const ContainerBreadcrumb({super.key, super.routes}); + + @override + Widget build(BuildContext context) { + if (routes?.isEmpty ?? true) { + return SizedBox.shrink(); + } + return buildContainerPageRoute(routes!); + } + + Widget buildContainerPageRoute(List route) { + return Container( + height: 24.h, + margin: EdgeInsets.symmetric(horizontal: 8.w, vertical: 4.h), + decoration: BoxDecoration(color: Color(0xFFE3E3E3), borderRadius: BorderRadius.circular(2.r)), + padding: EdgeInsets.symmetric(horizontal: 6.w), + child: ListView.separated( + scrollDirection: Axis.horizontal, + physics: NeverScrollableScrollPhysics(), + shrinkWrap: true, + itemBuilder: (context, index) => Center( + child: Text(route[index], style: AppFonts.yekan14.copyWith(color: AppColor.labelTextColor)), + ), + separatorBuilder: (context, index) => + Assets.vec.arrowLeftSvg.svg(height: 24.h, fit: BoxFit.fitHeight), + itemCount: route.length, + ), + ); + } +} + + diff --git a/packages/core/lib/presentation/widget/app_bar/r_app_bar.dart b/packages/core/lib/presentation/widget/base_page/widgets/r_app_bar.dart similarity index 55% rename from packages/core/lib/presentation/widget/app_bar/r_app_bar.dart rename to packages/core/lib/presentation/widget/base_page/widgets/r_app_bar.dart index edfeaea..ddb2202 100644 --- a/packages/core/lib/presentation/widget/app_bar/r_app_bar.dart +++ b/packages/core/lib/presentation/widget/base_page/widgets/r_app_bar.dart @@ -1,34 +1,50 @@ +import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:rasadyar_core/core.dart'; + class RAppBar extends StatelessWidget implements PreferredSizeWidget { - final String? title; - final String? iconTitle; - final Color backgroundColor; - final Color iconColor; - final bool hasBack; + final List? children; + final bool centerTitle; - final TextStyle? titleTextStyle; - final VoidCallback? onBackPressed; - final List? additionalActions; - final double? leadingWidth; - final Widget? leading; + + final Color backgroundColor; + + final bool isBase; + + final bool hasBack; + final VoidCallback? onBackTap; + final int? backId; + + final bool hasSearch; + final VoidCallback? onSearchTap; + + final bool hasNotification; + + final VoidCallback? onNotificationTap; + + final bool hasNews; + final VoidCallback? onNewsTap; + + /// Preferred size widget for the AppBar bottom. final PreferredSizeWidget? bottom; const RAppBar({ super.key, - this.title, - this.iconTitle, + this.children, this.backgroundColor = AppColor.blueNormal, - this.iconColor = Colors.white, - this.titleTextStyle, - this.onBackPressed, - this.additionalActions, - this.leading, this.hasBack = true, + this.hasSearch = false, + this.hasNews = false, + this.hasNotification= false, + this.isBase = false, this.centerTitle = false, - this.leadingWidth, this.bottom, + this.backId, + this.onBackTap, + this.onSearchTap, + this.onNewsTap, + this.onNotificationTap, }); @override @@ -39,46 +55,64 @@ class RAppBar extends StatelessWidget implements PreferredSizeWidget { elevation: 0, excludeHeaderSemantics: true, scrolledUnderElevation: 0, - centerTitle: centerTitle, - titleTextStyle: titleTextStyle ?? AppFonts.yekan16.copyWith(color: Colors.white), - title: title != null - ? Row( - mainAxisAlignment: MainAxisAlignment.center, - mainAxisSize: MainAxisSize.min, - children: [ - Text(title!), - if (iconTitle != null) ...{const SizedBox(width: 8)}, - if (iconTitle != null) ...{SvgGenImage.vec(iconTitle!).svg(width: 24, height: 24)}, - ], - ) - : null, - leadingWidth: leadingWidth?.toDouble(), - leading: leading != null - ? Padding(padding: const EdgeInsets.only(right: 6), child: leading) - : null, - titleSpacing: 8, - actions: [ - if (additionalActions != null) ...additionalActions!, - if (hasBack) ...{ - GestureDetector( - onTap: onBackPressed ?? () => Get.back(), - child: Padding( - padding: const EdgeInsets.fromLTRB(10, 0, 2, 0), - child: Assets.vec.arrowLeftSvg.svg( - width: 24.w, - height: 24.h, - colorFilter: ColorFilter.mode(iconColor ?? Colors.white, BlendMode.srcIn), + title: Row( + mainAxisAlignment: _getMainAxisAlignment(), + mainAxisSize: MainAxisSize.min, + textDirection: TextDirection.rtl, // Support for RTL languages + children: [ + if (children != null) ...children!, + + if (hasNews || hasBack || hasSearch || hasNotification) const Spacer(), + + if (hasSearch) SearchWidget(), + if (hasSearch) SizedBox(width: 8.w), + + if (hasNews) + GestureDetector( + onTap: onNewsTap, + child: Badge.count( + count: 5, + child: Icon(CupertinoIcons.news_solid, color: Colors.white), ), ), - ), - }, - ], + if (hasNews) SizedBox(width: 8.w), + + if (hasNotification) + Badge.count(count: 2, child: Icon(CupertinoIcons.bell_fill, color: Colors.white)), + if (hasNotification) SizedBox(width: 8.w), + + if (hasBack) + GestureDetector( + onTap: onBackTap ?? () => Get.back(id: backId), + child: Padding( + padding: const EdgeInsets.fromLTRB(10, 0, 2, 0), + child: Assets.vec.arrowLeftSvg.svg( + width: 24.w, + height: 24.h, + colorFilter: ColorFilter.mode(Colors.white, BlendMode.srcIn), + ), + ), + ), + if (hasBack) SizedBox(width: 8.w), + ], + ), + titleSpacing: 2, bottom: bottom, ); } @override - Size get preferredSize => const Size.fromHeight(kToolbarHeight); + Size get preferredSize => Size.fromHeight(kToolbarHeight + (bottom?.preferredSize.height ?? 0)); + + MainAxisAlignment _getMainAxisAlignment() { + if (centerTitle) { + return MainAxisAlignment.center; + } else if (children != null && children!.isNotEmpty) { + return MainAxisAlignment.start; + } else { + return MainAxisAlignment.end; + } + } } class RAppBar2 extends StatelessWidget implements PreferredSizeWidget { diff --git a/packages/chicken/lib/presentation/widget/search/view.dart b/packages/core/lib/presentation/widget/base_page/widgets/search_widget.dart similarity index 69% rename from packages/chicken/lib/presentation/widget/search/view.dart rename to packages/core/lib/presentation/widget/base_page/widgets/search_widget.dart index 1e09056..d0d7ecb 100644 --- a/packages/chicken/lib/presentation/widget/search/view.dart +++ b/packages/core/lib/presentation/widget/base_page/widgets/search_widget.dart @@ -1,27 +1,8 @@ import 'package:flutter/material.dart'; import 'package:rasadyar_core/core.dart'; -import 'logic.dart'; - -class SearchWidget extends StatefulWidget { - const SearchWidget({super.key, this.onSearchChanged}); - - final void Function(String?)? onSearchChanged; - - @override - State createState() => _SearchWidgetState(); -} - -class _SearchWidgetState extends State { - late final SearchLogic controller; - final TextEditingController textEditingController = TextEditingController(); - - @override - void initState() { - super.initState(); - controller = Get.find(); - controller.setSearchCallback(widget.onSearchChanged); - } +class SearchWidget extends GetView { + const SearchWidget({super.key}); @override Widget build(BuildContext context) { @@ -48,10 +29,7 @@ class _SearchWidgetState extends State { ) : IconButton( onPressed: () { - textEditingController.clear(); - controller.searchValue.value = null; - controller.isSearchSelected.value = false; - widget.onSearchChanged?.call(null); + controller.clearSearch(); }, enableFeedback: true, padding: EdgeInsets.zero, @@ -70,7 +48,7 @@ class _SearchWidgetState extends State { hintStyle: AppFonts.yekan16.copyWith(color: AppColor.blueNormal), filledColor: Colors.white, filled: true, - controller: textEditingController, + controller: controller.textEditingController, onChanged: (val) => controller.searchValue.value = val, ), ), diff --git a/packages/core/lib/presentation/widget/widget.dart b/packages/core/lib/presentation/widget/widget.dart index 9000624..293674f 100644 --- a/packages/core/lib/presentation/widget/widget.dart +++ b/packages/core/lib/presentation/widget/widget.dart @@ -1,9 +1,17 @@ -export 'app_bar/r_app_bar.dart'; +export 'base_page/widgets/r_app_bar.dart'; export 'bottom_navigation/r_bottom_navigation.dart'; export 'bottom_navigation/wave_bottom_navigation.dart'; export 'bottom_sheet/base_bottom_sheet.dart'; export 'bottom_sheet/date_picker_bottom_sheet.dart'; export 'check_box/check_box_widget.dart'; +//base page +export 'base_page/view.dart'; +export 'base_page/logic.dart'; +export 'base_page/widgets/back_ground_widget.dart'; +export 'base_page/widgets/breadcrumb.dart'; +export 'base_page/widgets/search_widget.dart'; + + //buttons export 'buttons/buttons.dart'; export 'card/card_icon_widget.dart';