refactor : base page
This commit is contained in:
@@ -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<AssetGenImage> get values => [
|
||||
bgChickenPattern,
|
||||
chicken,
|
||||
innerSplash,
|
||||
liveChicken,
|
||||
outterSplash,
|
||||
placeHolder,
|
||||
selectRole,
|
||||
];
|
||||
List<AssetGenImage> get values => [chicken, innerSplash, outterSplash, placeHolder, selectRole];
|
||||
}
|
||||
|
||||
class $AssetsLogosGen {
|
||||
|
||||
31
packages/core/lib/presentation/widget/base_page/logic.dart
Normal file
31
packages/core/lib/presentation/widget/base_page/logic.dart
Normal file
@@ -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<String?>(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;
|
||||
}
|
||||
}
|
||||
91
packages/core/lib/presentation/widget/base_page/view.dart
Normal file
91
packages/core/lib/presentation/widget/base_page/view.dart
Normal file
@@ -0,0 +1,91 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:rasadyar_core/core.dart';
|
||||
|
||||
class BasePage extends GetView<BaseLogic> {
|
||||
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<String>? routes;
|
||||
final Breadcrumb? routesWidget;
|
||||
final List<Widget>? 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,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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),
|
||||
);
|
||||
}*/
|
||||
@@ -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<String>? 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<String> 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,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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<Widget>? children;
|
||||
|
||||
final bool centerTitle;
|
||||
final TextStyle? titleTextStyle;
|
||||
final VoidCallback? onBackPressed;
|
||||
final List<Widget>? 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 {
|
||||
@@ -0,0 +1,59 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:rasadyar_core/core.dart';
|
||||
|
||||
class SearchWidget extends GetView<BaseLogic> {
|
||||
const SearchWidget({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ObxValue((data) {
|
||||
return AnimatedContainer(
|
||||
duration: const Duration(milliseconds: 300),
|
||||
curve: Curves.easeInOut,
|
||||
height: data.value ? 40 : 0,
|
||||
child: Visibility(
|
||||
visible: data.value,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8),
|
||||
child: RTextField(
|
||||
height: 40,
|
||||
borderColor: AppColor.blackLight,
|
||||
suffixIcon: ObxValue(
|
||||
(data) => Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 8),
|
||||
child: (data.value == null)
|
||||
? Assets.vec.searchSvg.svg(
|
||||
width: 10,
|
||||
height: 10,
|
||||
colorFilter: ColorFilter.mode(AppColor.blueNormal, BlendMode.srcIn),
|
||||
)
|
||||
: IconButton(
|
||||
onPressed: () {
|
||||
controller.clearSearch();
|
||||
},
|
||||
enableFeedback: true,
|
||||
padding: EdgeInsets.zero,
|
||||
iconSize: 24,
|
||||
splashRadius: 50,
|
||||
icon: Assets.vec.closeCircleSvg.svg(
|
||||
width: 20,
|
||||
height: 20,
|
||||
colorFilter: ColorFilter.mode(AppColor.blueNormal, BlendMode.srcIn),
|
||||
),
|
||||
),
|
||||
),
|
||||
controller.searchValue,
|
||||
),
|
||||
hintText: 'جستجو کنید ...',
|
||||
hintStyle: AppFonts.yekan16.copyWith(color: AppColor.blueNormal),
|
||||
filledColor: Colors.white,
|
||||
filled: true,
|
||||
controller: controller.textEditingController,
|
||||
onChanged: (val) => controller.searchValue.value = val,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}, controller.isSearchSelected);
|
||||
}
|
||||
}
|
||||
@@ -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';
|
||||
|
||||
Reference in New Issue
Block a user