feat : new main page inspection
This commit is contained in:
@@ -11,7 +11,7 @@ class SupervisionFilterPage extends GetView<InspectorFilterLogic> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: RAppBar.noBack(title: 'نقشه', additionalActions: [_searchButton(), _filterButton()]),
|
||||
appBar: RAppBar(title: 'نقشه', additionalActions: [_searchButton(), _filterButton()]),
|
||||
body: PopScope(
|
||||
canPop: !controller.bottomSheetManager.isAnyVisible,
|
||||
onPopInvokedWithResult: (didPop, result) {
|
||||
|
||||
151
packages/inspection/lib/presentation/inspection_map/logic.dart
Normal file
151
packages/inspection/lib/presentation/inspection_map/logic.dart
Normal file
@@ -0,0 +1,151 @@
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:rasadyar_core/core.dart';
|
||||
import 'package:rasadyar_inspection/data/utils/marker_generator.dart';
|
||||
import 'package:rasadyar_inspection/presentation/widget/base_page/logic.dart';
|
||||
|
||||
import '../filter/view.dart';
|
||||
|
||||
class InspectionMapLogic extends GetxController with GetTickerProviderStateMixin {
|
||||
|
||||
final BaseLogic baseLogic = Get.find<BaseLogic>();
|
||||
|
||||
Rx<LatLng> currentLocation = LatLng(35.824891, 50.948025).obs;
|
||||
RxList<LatLng> allMarkers = <LatLng>[].obs;
|
||||
RxList<LatLng> markers = <LatLng>[].obs;
|
||||
Timer? _debounceTimer;
|
||||
RxBool isLoading = false.obs;
|
||||
|
||||
RxInt filterIndex = 0.obs;
|
||||
RxInt showIndex = 0.obs;
|
||||
bool showSlideHint = true;
|
||||
|
||||
late Rx<SlidableController> slidController;
|
||||
|
||||
Rx<MapController> mapController = MapController().obs;
|
||||
late final AnimatedMapController animatedMapController;
|
||||
|
||||
late DraggableBottomSheetController filterBottomSheetController;
|
||||
late DraggableBottomSheetController selectedLocationBottomSheetController;
|
||||
late DraggableBottomSheetController detailsLocationBottomSheetController;
|
||||
late final BottomSheetManager bottomSheetManager;
|
||||
|
||||
Future<void> determineCurrentPosition() async {
|
||||
isLoading.value = true;
|
||||
final position = await Geolocator.getCurrentPosition(
|
||||
locationSettings: AndroidSettings(accuracy: LocationAccuracy.best),
|
||||
);
|
||||
final latLng = LatLng(position.latitude, position.longitude);
|
||||
|
||||
currentLocation.value = latLng;
|
||||
markers.add(latLng);
|
||||
animatedMapController.animateTo(
|
||||
dest: latLng,
|
||||
zoom: 18,
|
||||
curve: Curves.easeInOut,
|
||||
duration: const Duration(seconds: 1),
|
||||
);
|
||||
isLoading.value = false;
|
||||
}
|
||||
|
||||
void debouncedUpdateVisibleMarkers({required LatLng center}) {
|
||||
_debounceTimer?.cancel();
|
||||
_debounceTimer = Timer(const Duration(milliseconds: 300), () {
|
||||
final filtered = filterNearbyMarkers({
|
||||
'markers': allMarkers,
|
||||
'centerLat': center.latitude,
|
||||
'centerLng': center.longitude,
|
||||
'radius': 2000.0,
|
||||
});
|
||||
|
||||
markers.addAll(filtered);
|
||||
});
|
||||
}
|
||||
|
||||
List<LatLng> filterNearbyMarkers(Map<String, dynamic> args) {
|
||||
final List<LatLng> rawMarkers = args['markers'];
|
||||
final double centerLat = args['centerLat'];
|
||||
final double centerLng = args['centerLng'];
|
||||
final double radiusInMeters = args['radius'];
|
||||
final center = LatLng(centerLat, centerLng);
|
||||
final distance = Distance();
|
||||
|
||||
return rawMarkers
|
||||
.where((marker) => distance(center, marker) <= radiusInMeters)
|
||||
.toList();
|
||||
}
|
||||
|
||||
Future<void> generatedMarkers() async {
|
||||
final generatedMarkers = await generateLocationsUsingCompute(100000);
|
||||
allMarkers.value = generatedMarkers;
|
||||
}
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
animatedMapController = AnimatedMapController(
|
||||
vsync: this,
|
||||
duration: const Duration(milliseconds: 500),
|
||||
curve: Curves.easeInOut,
|
||||
cancelPreviousAnimations: true,
|
||||
);
|
||||
|
||||
filterBottomSheetController = DraggableBottomSheetController(
|
||||
initialHeight: 350,
|
||||
minHeight: 200,
|
||||
maxHeight: Get.height * 0.5,
|
||||
);
|
||||
|
||||
selectedLocationBottomSheetController = DraggableBottomSheetController(
|
||||
initialHeight: 200,
|
||||
minHeight: 100,
|
||||
maxHeight: 200,
|
||||
);
|
||||
|
||||
|
||||
detailsLocationBottomSheetController = DraggableBottomSheetController(
|
||||
initialHeight: Get.height * 0.5,
|
||||
minHeight: Get.height * 0.37,
|
||||
maxHeight: Get.height * 0.5,
|
||||
);
|
||||
|
||||
slidController = SlidableController(this).obs;
|
||||
bottomSheetManager = BottomSheetManager({
|
||||
filterBottomSheetController:
|
||||
() => filterWidget(filterIndex: filterIndex, showIndex: showIndex),
|
||||
selectedLocationBottomSheetController:
|
||||
() => selectedLocationWidget(
|
||||
showHint:
|
||||
selectedLocationBottomSheetController.isVisible.value &&
|
||||
showSlideHint,
|
||||
sliderController: slidController.value,
|
||||
trigger: triggerSlidableAnimation,
|
||||
toggle: selectedLocationBottomSheetController.toggle,
|
||||
),
|
||||
detailsLocationBottomSheetController: () => markerDetailsWidget(),
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void onReady() {
|
||||
super.onReady();
|
||||
determineCurrentPosition();
|
||||
generatedMarkers();
|
||||
}
|
||||
|
||||
Future<void> triggerSlidableAnimation() async {
|
||||
await Future.delayed(Duration(milliseconds: 200));
|
||||
await slidController.value.openEndActionPane();
|
||||
await Future.delayed(Duration(milliseconds: 200));
|
||||
await slidController.value.close();
|
||||
showSlideHint = false;
|
||||
}
|
||||
|
||||
@override
|
||||
void onClose() {
|
||||
slidController.close();
|
||||
super.onClose();
|
||||
}
|
||||
}
|
||||
650
packages/inspection/lib/presentation/inspection_map/view.dart
Normal file
650
packages/inspection/lib/presentation/inspection_map/view.dart
Normal file
@@ -0,0 +1,650 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:rasadyar_core/core.dart';
|
||||
import 'package:rasadyar_inspection/presentation/routes/app_routes.dart';
|
||||
import 'package:rasadyar_inspection/presentation/widget/base_page/view.dart';
|
||||
import 'package:rasadyar_inspection/presentation/widget/search.dart';
|
||||
|
||||
import 'logic.dart';
|
||||
|
||||
class InspectionMapPage extends GetView<InspectionMapLogic> {
|
||||
const InspectionMapPage({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BasePage(
|
||||
hasSearch: true,
|
||||
hasFilter: true,
|
||||
hasBack: false,
|
||||
|
||||
defaultSearch: false,
|
||||
filteringWidget: filterWidget(showIndex: 3.obs, filterIndex: 5.obs),
|
||||
onSearchTap: () {
|
||||
controller.baseLogic.isSearchSelected.value = !controller.baseLogic.isSearchSelected.value;
|
||||
},
|
||||
|
||||
widgets: [_buildMap()],
|
||||
floatingActionButton: _buildGpsButton(),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildMap() {
|
||||
return Expanded(
|
||||
child: Stack(
|
||||
fit: StackFit.expand,
|
||||
children: [
|
||||
ObxValue((currentLocation) {
|
||||
return FlutterMap(
|
||||
mapController: controller.animatedMapController.mapController,
|
||||
options: MapOptions(
|
||||
initialCenter: currentLocation.value,
|
||||
initialZoom: 18,
|
||||
onPositionChanged: (camera, hasGesture) {
|
||||
controller.debouncedUpdateVisibleMarkers(center: camera.center);
|
||||
},
|
||||
),
|
||||
children: [
|
||||
TileLayer(urlTemplate: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png'),
|
||||
ObxValue((markers) {
|
||||
return MarkerLayer(
|
||||
markers: markers
|
||||
.map(
|
||||
(e) => markerWidget(
|
||||
marker: e,
|
||||
onTap: () {
|
||||
Get.bottomSheet(
|
||||
selectedLocationWidget(
|
||||
showHint: false,
|
||||
sliderController: controller.slidController.value,
|
||||
trigger: () {},
|
||||
toggle: () {},
|
||||
),
|
||||
isScrollControlled: true,
|
||||
enableDrag: true,
|
||||
backgroundColor: Colors.transparent,
|
||||
);
|
||||
},
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
);
|
||||
}, controller.markers),
|
||||
],
|
||||
);
|
||||
}, controller.currentLocation),
|
||||
|
||||
Positioned(
|
||||
top: 10,
|
||||
left: 20,
|
||||
right: 20,
|
||||
child: ObxValue((data) {
|
||||
if (data.value) {
|
||||
return SearchWidget(
|
||||
onSearchChanged: (data) {
|
||||
controller.baseLogic.searchValue.value = data;
|
||||
},
|
||||
);
|
||||
} else {
|
||||
return SizedBox.shrink();
|
||||
}
|
||||
}, controller.baseLogic.isSearchSelected),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildGpsButton() {
|
||||
return ObxValue((data) {
|
||||
return RFab(
|
||||
backgroundColor: AppColor.greenNormal,
|
||||
isLoading: data.value,
|
||||
icon: Assets.vec.gpsSvg.svg(width: 40.w, height: 40.h),
|
||||
onPressed: () async => await controller.determineCurrentPosition(),
|
||||
);
|
||||
}, controller.isLoading);
|
||||
}
|
||||
}
|
||||
|
||||
Marker markerWidget({required LatLng marker, required VoidCallback onTap}) {
|
||||
return Marker(
|
||||
point: marker,
|
||||
child: GestureDetector(
|
||||
onTap: onTap,
|
||||
behavior: HitTestBehavior.opaque,
|
||||
child: SizedBox(
|
||||
width: 36,
|
||||
height: 36,
|
||||
child: Assets.vec.mapMarkerSvg.svg(width: 30, height: 30),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget filterWidget({required RxInt filterIndex, required RxInt showIndex}) {
|
||||
return BaseBottomSheet(
|
||||
height: Get.height * 0.5,
|
||||
child: Column(
|
||||
spacing: 16,
|
||||
children: [
|
||||
SizedBox(height: 1),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||
spacing: 16,
|
||||
children: [
|
||||
cardWithLabel(
|
||||
title: 'اصناف',
|
||||
count: 1234567,
|
||||
icon: Assets.vec.shopSvg.svg(width: 24.w, height: 24.h),
|
||||
backgroundColor: AppColor.greenLight,
|
||||
backgroundBadgeColor: AppColor.greenLightActive,
|
||||
),
|
||||
cardWithLabel(
|
||||
title: 'دامداران',
|
||||
count: 1234567,
|
||||
icon: Assets.vec.peopleSvg.svg(width: 24.w, height: 24.h),
|
||||
|
||||
backgroundColor: AppColor.blueLight,
|
||||
backgroundBadgeColor: AppColor.blueLightActive,
|
||||
),
|
||||
cardWithLabel(
|
||||
title: 'مرغداران',
|
||||
count: 1234567,
|
||||
icon: Assets.vec.profile2Svg.svg(width: 24.w, height: 24.h),
|
||||
backgroundColor: AppColor.redLight,
|
||||
backgroundBadgeColor: AppColor.redLightActive,
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
spacing: 12,
|
||||
children: [
|
||||
Text(
|
||||
'فیلتر نمایش',
|
||||
textAlign: TextAlign.center,
|
||||
style: AppFonts.yekan13.copyWith(color: AppColor.blueNormal),
|
||||
),
|
||||
ObxValue((data) {
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
spacing: 8,
|
||||
children: [
|
||||
customChip(
|
||||
isSelected: data.value == 0,
|
||||
onTap: (data) {
|
||||
filterIndex.value = data;
|
||||
},
|
||||
index: 0,
|
||||
title: 'دامداران',
|
||||
),
|
||||
customChip(
|
||||
isSelected: data.value == 1,
|
||||
title: 'مرغداران',
|
||||
onTap: (data) {
|
||||
filterIndex.value = data;
|
||||
},
|
||||
index: 1,
|
||||
),
|
||||
customChip(
|
||||
isSelected: data.value == 2,
|
||||
title: 'اصناف',
|
||||
onTap: (data) {
|
||||
filterIndex.value = data;
|
||||
},
|
||||
index: 2,
|
||||
),
|
||||
],
|
||||
);
|
||||
}, filterIndex),
|
||||
],
|
||||
),
|
||||
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
spacing: 12,
|
||||
children: [
|
||||
Text(
|
||||
'نمایش',
|
||||
textAlign: TextAlign.center,
|
||||
style: AppFonts.yekan13.copyWith(color: AppColor.blueNormal),
|
||||
),
|
||||
ObxValue((data) {
|
||||
return SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
spacing: 8,
|
||||
children: [
|
||||
customChip(
|
||||
isSelected: data.value == 0,
|
||||
title: 'نمایش همه',
|
||||
onTap: (data) {
|
||||
showIndex.value = data;
|
||||
},
|
||||
index: 0,
|
||||
),
|
||||
customChip(
|
||||
isSelected: data.value == 1,
|
||||
title: 'دارای تراکنش',
|
||||
onTap: (data) {
|
||||
showIndex.value = data;
|
||||
},
|
||||
index: 1,
|
||||
),
|
||||
customChip(
|
||||
isSelected: data.value == 2,
|
||||
title: 'بازرسی شده ها',
|
||||
onTap: (data) {
|
||||
showIndex.value = data;
|
||||
},
|
||||
index: 2,
|
||||
),
|
||||
customChip(
|
||||
isSelected: data.value == 3,
|
||||
title: 'بازرسی نشده ها',
|
||||
onTap: (data) {
|
||||
showIndex.value = data;
|
||||
},
|
||||
index: 3,
|
||||
),
|
||||
customChip(
|
||||
isSelected: data.value == 4,
|
||||
title: 'متخلفین',
|
||||
onTap: (data) {
|
||||
showIndex.value = data;
|
||||
},
|
||||
index: 4,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}, showIndex),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget cardWithLabel({
|
||||
required String title,
|
||||
required int count,
|
||||
String unit = 'عدد',
|
||||
required Widget icon,
|
||||
required Color backgroundColor,
|
||||
required Color backgroundBadgeColor,
|
||||
}) {
|
||||
return SizedBox(
|
||||
width: 114.w,
|
||||
height: 115.h,
|
||||
child: Stack(
|
||||
clipBehavior: Clip.antiAlias,
|
||||
alignment: Alignment.topCenter,
|
||||
children: [
|
||||
Positioned(
|
||||
bottom: 0,
|
||||
right: 0,
|
||||
left: 0,
|
||||
child: Container(
|
||||
width: 114.w,
|
||||
height: 91.h,
|
||||
clipBehavior: Clip.antiAlias,
|
||||
decoration: BoxDecoration(
|
||||
color: backgroundColor,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
border: Border.all(width: 0.25, color: AppColor.blackLightHover),
|
||||
),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
spacing: 6,
|
||||
children: [
|
||||
Text(title, style: AppFonts.yekan12.copyWith(color: AppColor.textColor)),
|
||||
Text(
|
||||
count.separatedByComma,
|
||||
style: AppFonts.yekan16.copyWith(color: AppColor.textColor),
|
||||
),
|
||||
Text(unit, style: AppFonts.yekan12.copyWith(color: AppColor.textColor)),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
top: 5.h,
|
||||
child: Container(
|
||||
width: 32.w,
|
||||
height: 32.h,
|
||||
padding: EdgeInsets.all(4),
|
||||
decoration: BoxDecoration(
|
||||
color: backgroundBadgeColor,
|
||||
borderRadius: BorderRadius.circular(50),
|
||||
border: Border.all(color: AppColor.borderColor, width: 0.25),
|
||||
),
|
||||
child: Center(child: icon),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget markerDetailsWidget() {
|
||||
return Container(
|
||||
clipBehavior: Clip.antiAlias,
|
||||
margin: EdgeInsets.all(35),
|
||||
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 10),
|
||||
decoration: ShapeDecoration(
|
||||
color: Colors.white,
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
|
||||
),
|
||||
child: Column(
|
||||
spacing: 15,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
spacing: 12,
|
||||
children: [
|
||||
Text(
|
||||
'داود خرم پور',
|
||||
textAlign: TextAlign.center,
|
||||
style: AppFonts.yekan16.copyWith(color: AppColor.darkGreyDarkHover),
|
||||
),
|
||||
Spacer(),
|
||||
vecWidgetWithOnTap(
|
||||
child: Assets.vec.mapSvg.svg(),
|
||||
onTap: () {
|
||||
Get.toNamed(InspectionRoutes.inspectionLocationDetails);
|
||||
},
|
||||
width: 24,
|
||||
height: 24,
|
||||
color: AppColor.blueNormal,
|
||||
),
|
||||
vecWidgetWithOnTap(
|
||||
child: Assets.vec.messageAddSvg.svg(),
|
||||
width: 24,
|
||||
height: 24,
|
||||
color: AppColor.greenNormal,
|
||||
onTap: () {
|
||||
Get.toNamed(InspectionRoutes.inspectionAddSupervision);
|
||||
},
|
||||
),
|
||||
|
||||
vecWidgetWithOnTap(
|
||||
child: Assets.vec.securityTimeSvg.svg(),
|
||||
color: AppColor.warning,
|
||||
height: 24,
|
||||
width: 24,
|
||||
onTap: () {},
|
||||
),
|
||||
],
|
||||
),
|
||||
Container(
|
||||
height: 32,
|
||||
clipBehavior: Clip.antiAlias,
|
||||
padding: EdgeInsets.symmetric(horizontal: 10, vertical: 4),
|
||||
decoration: ShapeDecoration(
|
||||
color: AppColor.blueLight,
|
||||
shape: RoundedRectangleBorder(
|
||||
side: BorderSide(width: 1, color: AppColor.blueLightHover),
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
'باقی مانده',
|
||||
textAlign: TextAlign.center,
|
||||
style: AppFonts.yekan14.copyWith(color: AppColor.darkGreyDarkHover),
|
||||
),
|
||||
Text(
|
||||
'0 کیلوگرم',
|
||||
textAlign: TextAlign.center,
|
||||
style: AppFonts.yekan14.copyWith(color: AppColor.darkGreyDarkHover),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
'شماره همراه',
|
||||
textAlign: TextAlign.center,
|
||||
style: AppFonts.yekan14.copyWith(color: AppColor.darkGreyDarkHover),
|
||||
),
|
||||
Text(
|
||||
'0326598653',
|
||||
textAlign: TextAlign.center,
|
||||
style: AppFonts.yekan14.copyWith(color: AppColor.darkGreyDarkHover),
|
||||
),
|
||||
],
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
|
||||
children: [
|
||||
Text(
|
||||
'آخرین فعالیت',
|
||||
textAlign: TextAlign.center,
|
||||
style: AppFonts.yekan14.copyWith(color: AppColor.darkGreyDarkHover),
|
||||
),
|
||||
|
||||
Text(
|
||||
'1409/12/12',
|
||||
textAlign: TextAlign.center,
|
||||
style: AppFonts.yekan14.copyWith(color: AppColor.darkGreyDarkHover),
|
||||
),
|
||||
],
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
|
||||
children: [
|
||||
Text(
|
||||
'موجودی',
|
||||
textAlign: TextAlign.center,
|
||||
style: AppFonts.yekan14.copyWith(color: AppColor.darkGreyDarkHover),
|
||||
),
|
||||
Text(
|
||||
'5کیلوگرم',
|
||||
textAlign: TextAlign.center,
|
||||
style: AppFonts.yekan14.copyWith(color: AppColor.darkGreyDarkHover),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
...List.generate(
|
||||
5,
|
||||
(index) => Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
|
||||
children: [
|
||||
Text(
|
||||
'فروش رفته',
|
||||
textAlign: TextAlign.center,
|
||||
style: AppFonts.yekan14.copyWith(color: AppColor.darkGreyDarkHover),
|
||||
),
|
||||
Text(
|
||||
'0 کیلوگرم',
|
||||
textAlign: TextAlign.center,
|
||||
style: AppFonts.yekan14.copyWith(color: AppColor.darkGreyDarkHover),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget customChip({
|
||||
bool isSelected = false,
|
||||
required String title,
|
||||
required int index,
|
||||
required Function(int) onTap,
|
||||
}) {
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
onTap.call(index);
|
||||
},
|
||||
child: Container(
|
||||
height: 32.h,
|
||||
padding: EdgeInsets.symmetric(horizontal: 14.w, vertical: 8.h),
|
||||
clipBehavior: Clip.antiAlias,
|
||||
decoration: BoxDecoration(
|
||||
color: AppColor.whiteGreyNormal,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
border: Border.all(
|
||||
width: 1,
|
||||
color: isSelected ? AppColor.blueNormal : AppColor.blackLightActive,
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
|
||||
children: [
|
||||
SizedBox(
|
||||
width: 12.w,
|
||||
height: 12.h,
|
||||
child: Transform.scale(
|
||||
scale: 0.70,
|
||||
child: Checkbox(
|
||||
value: isSelected,
|
||||
side: BorderSide(color: AppColor.whiteDarkHover, width: 1),
|
||||
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||
visualDensity: VisualDensity.compact,
|
||||
overlayColor: WidgetStateProperty.all<Color>(AppColor.blueNormal),
|
||||
fillColor: WidgetStateProperty.resolveWith((states) {
|
||||
if (states.contains(WidgetState.selected)) {
|
||||
return AppColor.blueNormal;
|
||||
} else {
|
||||
return AppColor.whiteGreyNormal;
|
||||
}
|
||||
}),
|
||||
|
||||
onChanged: (value) {
|
||||
onTap.call(index);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(width: 8.w),
|
||||
Text(
|
||||
title,
|
||||
textAlign: TextAlign.center,
|
||||
style: AppFonts.yekan12.copyWith(
|
||||
color: isSelected ? AppColor.blueNormal : AppColor.whiteDarkHover,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget selectedLocationWidget({
|
||||
required bool showHint,
|
||||
required SlidableController sliderController,
|
||||
required VoidCallback trigger,
|
||||
required VoidCallback toggle,
|
||||
}) {
|
||||
if (showHint) {
|
||||
trigger.call();
|
||||
}
|
||||
return BaseBottomSheet(
|
||||
height: 150.h,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 30, vertical: 20),
|
||||
child: Slidable(
|
||||
key: Key('selectedLocationWidget'),
|
||||
controller: sliderController,
|
||||
endActionPane: ActionPane(
|
||||
motion: StretchMotion(),
|
||||
children: [
|
||||
CustomSlidableAction(
|
||||
onPressed: (context) {
|
||||
Get.toNamed(InspectionRoutes.inspectionLocationDetails);
|
||||
},
|
||||
backgroundColor: AppColor.blueNormal,
|
||||
foregroundColor: Colors.white,
|
||||
padding: EdgeInsets.all(16),
|
||||
borderRadius: BorderRadius.only(
|
||||
bottomRight: Radius.circular(8),
|
||||
topRight: Radius.circular(8),
|
||||
),
|
||||
child: Assets.vec.mapSvg.svg(width: 24, height: 24),
|
||||
),
|
||||
CustomSlidableAction(
|
||||
onPressed: (context) {
|
||||
Get.toNamed(InspectionRoutes.inspectionAddSupervision);
|
||||
},
|
||||
backgroundColor: AppColor.greenNormal,
|
||||
padding: EdgeInsets.all(16),
|
||||
child: Assets.vec.messageAddSvg.svg(),
|
||||
),
|
||||
CustomSlidableAction(
|
||||
onPressed: (context) {},
|
||||
backgroundColor: AppColor.warning,
|
||||
padding: EdgeInsets.all(16),
|
||||
borderRadius: BorderRadius.only(
|
||||
bottomLeft: Radius.circular(8),
|
||||
topLeft: Radius.circular(8),
|
||||
),
|
||||
child: Assets.vec.securityTimeSvg.svg(),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: GestureDetector(
|
||||
onTap: toggle,
|
||||
child: Container(
|
||||
height: 58,
|
||||
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 15),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
border: Border.all(width: 1, color: AppColor.blackLightHover),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Column(
|
||||
children: [
|
||||
Text(
|
||||
'داود خرم مهری پور',
|
||||
style: AppFonts.yekan10.copyWith(color: AppColor.blueNormal),
|
||||
),
|
||||
Text(
|
||||
'گوشت و مرغ',
|
||||
style: AppFonts.yekan12.copyWith(color: AppColor.darkGreyDarkHover),
|
||||
),
|
||||
],
|
||||
),
|
||||
Column(
|
||||
children: [
|
||||
Text(
|
||||
'باقی مانده',
|
||||
style: AppFonts.yekan10.copyWith(color: AppColor.blueNormal),
|
||||
),
|
||||
Text(
|
||||
'0 کیلوگرم',
|
||||
style: AppFonts.yekan12.copyWith(color: AppColor.darkGreyDarkHover),
|
||||
),
|
||||
],
|
||||
),
|
||||
Assets.vec.scanBarcodeSvg.svg(),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -2,13 +2,14 @@ import 'package:flutter/material.dart';
|
||||
import 'package:rasadyar_core/core.dart' ;
|
||||
import 'package:rasadyar_inspection/presentation/action/view.dart';
|
||||
import 'package:rasadyar_inspection/presentation/filter/view.dart';
|
||||
import 'package:rasadyar_inspection/presentation/inspection_map/view.dart';
|
||||
import 'package:rasadyar_inspection/presentation/profile/view.dart';
|
||||
|
||||
enum ErrorLocationType { serviceDisabled, permissionDenied, none }
|
||||
|
||||
class RootLogic extends GetxController {
|
||||
RxInt currentIndex = 0.obs;
|
||||
List<Widget> pages = [SupervisionFilterPage(), ActionPage(), ProfilePage()];
|
||||
List<Widget> pages = [InspectionMapPage(), ActionPage(), ProfilePage()];
|
||||
RxList<ErrorLocationType> errorLocationType = RxList();
|
||||
|
||||
Stream<bool> listenToLocationServiceStatus() {
|
||||
|
||||
@@ -8,168 +8,49 @@ class RootPage extends GetView<RootLogic> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
body: Stack(
|
||||
children: [
|
||||
ObxValue((errorType) {
|
||||
if (errorType.isNotEmpty) {
|
||||
if (errorType.contains(ErrorLocationType.serviceDisabled)) {
|
||||
Future.microtask(() {
|
||||
Get.defaultDialog(
|
||||
title: 'خطا',
|
||||
content: const Text('سرویس مکانیابی غیرفعال است'),
|
||||
cancel: ROutlinedElevated(
|
||||
text: 'بررسی مجدد',
|
||||
width: 120,
|
||||
textStyle: AppFonts.yekan16,
|
||||
onPressed: () async {
|
||||
var service = await controller.locationServiceEnabled();
|
||||
eLog(service);
|
||||
if (service) {
|
||||
controller.errorLocationType.remove(
|
||||
ErrorLocationType.serviceDisabled,
|
||||
);
|
||||
Get.back();
|
||||
}
|
||||
// Don't call Get.back() if service is still disabled
|
||||
},
|
||||
),
|
||||
confirm: RElevated(
|
||||
text: 'روشن کردن',
|
||||
textStyle: AppFonts.yekan16,
|
||||
width: 120,
|
||||
onPressed: () async {
|
||||
var res = await Geolocator.openLocationSettings();
|
||||
if (res) {
|
||||
var service =
|
||||
await controller.locationServiceEnabled();
|
||||
if (service) {
|
||||
controller.errorLocationType.remove(
|
||||
ErrorLocationType.serviceDisabled,
|
||||
);
|
||||
Get.back();
|
||||
}
|
||||
}
|
||||
},
|
||||
),
|
||||
return ObxValue((currentIndex) {
|
||||
return Scaffold(
|
||||
body: ObxValue(
|
||||
(currentIndex) => IndexedStack(index: currentIndex.value, children: controller.pages),
|
||||
controller.currentIndex,
|
||||
),
|
||||
bottomNavigationBar: RBottomNavigation(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
items: [
|
||||
RBottomNavigationItem(
|
||||
label: 'نقشه',
|
||||
icon: Assets.vec.mapSvg.path,
|
||||
isSelected: currentIndex.value == 0,
|
||||
onTap: () {
|
||||
Get.nestedKey(1)?.currentState?.popUntil((route) => route.isFirst);
|
||||
|
||||
contentPadding: EdgeInsets.all(8),
|
||||
onWillPop: () async {
|
||||
return controller.errorLocationType.isEmpty;
|
||||
},
|
||||
barrierDismissible: false,
|
||||
);
|
||||
});
|
||||
} else {
|
||||
Future.microtask(() {
|
||||
Get.defaultDialog(
|
||||
title: 'خطا',
|
||||
content: const Text(
|
||||
' دسترسی به سرویس مکانیابی غیرفعال است',
|
||||
),
|
||||
cancel: ROutlinedElevated(
|
||||
text: 'بررسی مجدد',
|
||||
width: 120,
|
||||
textStyle: AppFonts.yekan16,
|
||||
onPressed: () async {
|
||||
await controller.checkPermission();
|
||||
},
|
||||
),
|
||||
confirm: RElevated(
|
||||
text: 'اجازه دادن',
|
||||
textStyle: AppFonts.yekan16,
|
||||
width: 120,
|
||||
onPressed: () async {
|
||||
var res = await controller.checkPermission(
|
||||
request: true,
|
||||
);
|
||||
if (res) {
|
||||
controller.errorLocationType.remove(
|
||||
ErrorLocationType.permissionDenied,
|
||||
);
|
||||
Get.back();
|
||||
}
|
||||
},
|
||||
),
|
||||
controller.changePage(0);
|
||||
},
|
||||
),
|
||||
RBottomNavigationItem(
|
||||
label: 'اقدام',
|
||||
icon: Assets.vec.settingSvg.path,
|
||||
isSelected: currentIndex.value == 1,
|
||||
onTap: () {
|
||||
Get.nestedKey(0)?.currentState?.popUntil((route) => route.isFirst);
|
||||
controller.changePage(1);
|
||||
},
|
||||
),
|
||||
|
||||
contentPadding: EdgeInsets.all(8),
|
||||
onWillPop: () async {
|
||||
return controller.errorLocationType.isEmpty;
|
||||
},
|
||||
barrierDismissible: false,
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
return const SizedBox.shrink();
|
||||
}, controller.errorLocationType),
|
||||
RBottomNavigationItem(
|
||||
label: 'پروفایل',
|
||||
icon: Assets.vec.profileCircleSvg.path,
|
||||
isSelected: currentIndex.value == 4,
|
||||
onTap: () {
|
||||
Get.nestedKey(1)?.currentState?.popUntil((route) => route.isFirst);
|
||||
Get.nestedKey(0)?.currentState?.popUntil((route) => route.isFirst);
|
||||
|
||||
ObxValue(
|
||||
(currentIndex) => IndexedStack(
|
||||
index: currentIndex.value,
|
||||
children: controller.pages,
|
||||
controller.changePage(4);
|
||||
},
|
||||
),
|
||||
controller.currentIndex,
|
||||
),
|
||||
|
||||
],
|
||||
),
|
||||
bottomNavigationBar: WaveBottomNavigation(
|
||||
items: [
|
||||
WaveBottomNavigationItem(title: 'خانه', icon: Assets.vec.mapSvg.svg(
|
||||
width: 32,
|
||||
height: 32,
|
||||
colorFilter: ColorFilter.mode(Colors.white, BlendMode.srcIn),
|
||||
)),
|
||||
WaveBottomNavigationItem(
|
||||
title: 'عملیات',
|
||||
icon: Assets.vec.userSvg.svg(
|
||||
width: 32,
|
||||
height: 32,
|
||||
colorFilter: ColorFilter.mode(Colors.white, BlendMode.srcIn),
|
||||
),
|
||||
),
|
||||
WaveBottomNavigationItem(
|
||||
title: 'افزودن',
|
||||
icon: Assets.vec.addSvg.svg(
|
||||
width: 32,
|
||||
height: 32,
|
||||
colorFilter: ColorFilter.mode(Colors.white, BlendMode.srcIn),
|
||||
),
|
||||
),
|
||||
WaveBottomNavigationItem(
|
||||
title: 'آمار',
|
||||
icon: Assets.vec.diagramSvg.svg(width: 32,height: 32,colorFilter: ColorFilter.mode(Colors.white, BlendMode.srcIn),),
|
||||
),
|
||||
WaveBottomNavigationItem(
|
||||
title: 'تماس',
|
||||
icon: Assets.vec.callSvg.svg(
|
||||
width: 32,
|
||||
height: 32,
|
||||
colorFilter: ColorFilter.mode(Colors.white, BlendMode.srcIn),
|
||||
),
|
||||
),
|
||||
WaveBottomNavigationItem(
|
||||
title: 'مکان ',
|
||||
icon: Assets.vec.gpsSvg.svg(
|
||||
width: 32,
|
||||
height: 32,
|
||||
colorFilter: ColorFilter.mode(Colors.white, BlendMode.srcIn),
|
||||
),
|
||||
),
|
||||
WaveBottomNavigationItem(
|
||||
title: 'تاریخ',
|
||||
icon: Assets.vec.calendarSvg.svg(
|
||||
width: 32,
|
||||
height: 32,
|
||||
colorFilter: ColorFilter.mode(Colors.white, BlendMode.srcIn),
|
||||
),
|
||||
),
|
||||
],
|
||||
onPageChanged: (index) {
|
||||
controller.changePage(index);
|
||||
},
|
||||
),
|
||||
);
|
||||
],
|
||||
),
|
||||
);
|
||||
}, controller.currentIndex);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import 'package:rasadyar_inspection/presentation/add_supervision/view.dart';
|
||||
import 'package:rasadyar_inspection/presentation/display_information/logic.dart';
|
||||
import 'package:rasadyar_inspection/presentation/display_information/view.dart';
|
||||
import 'package:rasadyar_inspection/presentation/filter/logic.dart';
|
||||
import 'package:rasadyar_inspection/presentation/inspection_map/logic.dart';
|
||||
import 'package:rasadyar_inspection/presentation/location_details/logic.dart';
|
||||
import 'package:rasadyar_inspection/presentation/location_details/view.dart';
|
||||
import 'package:rasadyar_inspection/presentation/profile/logic.dart';
|
||||
@@ -17,18 +18,21 @@ import 'package:rasadyar_inspection/presentation/registration_of_violation/view.
|
||||
import 'package:rasadyar_inspection/presentation/root/logic.dart';
|
||||
import 'package:rasadyar_inspection/presentation/root/view.dart';
|
||||
import 'package:rasadyar_inspection/presentation/routes/app_routes.dart';
|
||||
import 'package:rasadyar_inspection/presentation/widget/base_page/logic.dart';
|
||||
|
||||
sealed class InspectionPages {
|
||||
InspectionPages._();
|
||||
|
||||
static final pages = [
|
||||
GetPage(
|
||||
name: InspectionRoutes.inspection,
|
||||
name: InspectionRoutes.init,
|
||||
page: () => RootPage(),
|
||||
middlewares: [AuthMiddleware()],
|
||||
|
||||
binding: BindingsBuilder(() {
|
||||
Get.put(RootLogic());
|
||||
Get.put(InspectorFilterLogic());
|
||||
Get.lazyPut(()=>InspectionMapLogic());
|
||||
Get.lazyPut(()=>BaseLogic());
|
||||
Get.lazyPut(() => LocationDetailsLogic(), fenix: true);
|
||||
Get.lazyPut(() => ActionLogic(), fenix: true);
|
||||
Get.lazyPut(() => ProfileLogic(), fenix: true);
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
sealed class InspectionRoutes {
|
||||
InspectionRoutes._();
|
||||
|
||||
static const inspection = '/supervision';
|
||||
static const inspectionAction = '$inspection/action';
|
||||
static const inspectionUserProfile = '$inspection/userSettings';
|
||||
static const inspectionLocationDetails = '$inspection/locationDetails';
|
||||
static const init = '/supervision';
|
||||
static const inspectionAction = '$init/action';
|
||||
static const inspectionUserProfile = '$init/userSettings';
|
||||
static const inspectionLocationDetails = '$init/locationDetails';
|
||||
static const inspectionAddSupervision = '$inspectionLocationDetails/addSupervision';
|
||||
static const inspectionAddMobileInspector = '$inspectionLocationDetails/addMobileInspector';
|
||||
static const inspectionRegistrationOfViolation = '$inspectionAddSupervision/RegistrationOfViolation';
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:rasadyar_core/core.dart';
|
||||
import 'package:rasadyar_inspection/presentation/widget/base_page/logic.dart';
|
||||
|
||||
RAppBar inspectionAppBar({
|
||||
bool hasBack = true,
|
||||
bool hasFilter = true,
|
||||
bool hasSearch = true,
|
||||
bool isBase = false,
|
||||
VoidCallback? onBackPressed,
|
||||
GestureTapCallback? onFilterTap,
|
||||
GestureTapCallback? onSearchTap,
|
||||
}) {
|
||||
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.appBarInspectionSvg.svg(width: 24, height: 24),
|
||||
],
|
||||
),
|
||||
additionalActions: [
|
||||
if (!isBase && hasSearch) searchWidget(onSearchTap),
|
||||
SizedBox(width: 8),
|
||||
if (!isBase && hasFilter) filterWidget(onFilterTap),
|
||||
SizedBox(width: 8),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
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<BaseLogic>();
|
||||
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<BaseLogic>();
|
||||
return Visibility(
|
||||
visible: controller.searchValue.value != null,
|
||||
child: Container(
|
||||
width: 8,
|
||||
height: 8,
|
||||
decoration: const BoxDecoration(color: Colors.red, shape: BoxShape.circle),
|
||||
),
|
||||
);
|
||||
}),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import 'package:rasadyar_core/core.dart';
|
||||
|
||||
class BaseLogic extends GetxController {
|
||||
final RxBool isFilterSelected = false.obs;
|
||||
final RxBool isSearchSelected = false.obs;
|
||||
final RxnString searchValue = RxnString();
|
||||
|
||||
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 toggleFilter() {
|
||||
isFilterSelected.value = !isFilterSelected.value;
|
||||
}
|
||||
|
||||
void toggleSearch() {
|
||||
isSearchSelected.value = !isSearchSelected.value;
|
||||
}
|
||||
}
|
||||
146
packages/inspection/lib/presentation/widget/base_page/view.dart
Normal file
146
packages/inspection/lib/presentation/widget/base_page/view.dart
Normal file
@@ -0,0 +1,146 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:rasadyar_core/core.dart';
|
||||
import 'package:rasadyar_inspection/presentation/widget/app_bar/i_app_bar.dart';
|
||||
import 'package:rasadyar_inspection/presentation/widget/search.dart';
|
||||
|
||||
import 'logic.dart';
|
||||
|
||||
class BasePage extends StatefulWidget {
|
||||
const BasePage({
|
||||
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.defaultSearch = true,
|
||||
this.onBackPressed,
|
||||
this.onFilterTap,
|
||||
this.onSearchTap,
|
||||
this.filteringWidget,
|
||||
|
||||
});
|
||||
|
||||
final List<String>? routes;
|
||||
final Widget? routesWidget;
|
||||
final bool defaultSearch;
|
||||
final List<Widget> 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<BasePage> createState() => _BasePageState();
|
||||
}
|
||||
|
||||
class _BasePageState extends State<BasePage> {
|
||||
BaseLogic get controller => Get.find<BaseLogic>();
|
||||
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) {
|
||||
|
||||
final currentRoute = ModalRoute.of(context);
|
||||
if (currentRoute?.isCurrent != true) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
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: inspectionAppBar(
|
||||
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 ? () => controller.toggleSearch() : null,
|
||||
),
|
||||
body: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
//widget.routesWidget != null ? widget.routesWidget! : buildPageRoute(widget.routes!),
|
||||
if (!widget.isBase && widget.hasSearch && widget.defaultSearch) ...{
|
||||
SearchWidget(onSearchChanged: widget.onSearchChanged),
|
||||
},
|
||||
...widget.widgets,
|
||||
],
|
||||
),
|
||||
floatingActionButtonLocation:
|
||||
widget.floatingActionButtonLocation ?? FloatingActionButtonLocation.startFloat,
|
||||
floatingActionButton: widget.floatingActionButton,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
81
packages/inspection/lib/presentation/widget/search.dart
Normal file
81
packages/inspection/lib/presentation/widget/search.dart
Normal file
@@ -0,0 +1,81 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:rasadyar_core/core.dart';
|
||||
import 'package:rasadyar_inspection/presentation/widget/base_page/logic.dart';
|
||||
|
||||
|
||||
class SearchWidget extends StatefulWidget {
|
||||
const SearchWidget({super.key, this.onSearchChanged});
|
||||
|
||||
final void Function(String?)? onSearchChanged;
|
||||
|
||||
@override
|
||||
State<SearchWidget> createState() => _SearchWidgetState();
|
||||
}
|
||||
|
||||
class _SearchWidgetState extends State<SearchWidget> {
|
||||
late final BaseLogic controller;
|
||||
final TextEditingController textEditingController = TextEditingController();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
controller = Get.find<BaseLogic>();
|
||||
controller.setSearchCallback(widget.onSearchChanged);
|
||||
}
|
||||
|
||||
@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: () {
|
||||
textEditingController.clear();
|
||||
controller.searchValue.value = null;
|
||||
controller.isSearchSelected.value = false;
|
||||
widget.onSearchChanged?.call(null);
|
||||
},
|
||||
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: textEditingController,
|
||||
onChanged: (val) => controller.searchValue.value = val,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}, controller.isSearchSelected);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user