1 - search in map with api
2 - show details in selected location
This commit is contained in:
2025-07-30 12:31:47 +03:30
parent 2806301367
commit f563c6188e
25 changed files with 3960 additions and 337 deletions

View File

@@ -8,14 +8,20 @@ import 'package:rasadyar_inspection/data/repositories/inspection/inspection_repo
import 'package:rasadyar_inspection/injection/inspection_di.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>();
InspectionRepositoryImp inspectionRepository = diInspection.get<InspectionRepositoryImp>();
final distance = Distance();
Rx<LatLng> currentLocation = LatLng(34.798315281272544, 48.51479142983491).obs;
Rx<Resource<List<PoultryLocationModel>>> allPoultryLocation =
Resource<List<PoultryLocationModel>>.loading().obs;
Resource<List<PoultryLocationModel>>.initial().obs;
Rx<Resource<List<PoultryLocationModel>>> searchedPoultryLocation =
Resource<List<PoultryLocationModel>>.initial().obs;
RxList<Marker> markers = <Marker>[].obs;
RxList<PoultryLocationModel> markers2 = <PoultryLocationModel>[].obs;
@@ -28,19 +34,9 @@ class InspectionMapLogic extends GetxController with GetTickerProviderStateMixin
RxInt showIndex = 0.obs;
bool showSlideHint = true;
RxInt currentZoom = 15.obs;
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;
InspectionRepositoryImp inspectionRepository = diInspection.get<InspectionRepositoryImp>();
@override
void onInit() {
super.onInit();
@@ -50,38 +46,16 @@ class InspectionMapLogic extends GetxController with GetTickerProviderStateMixin
curve: Curves.easeInOut,
cancelPreviousAnimations: true,
);
fetchAllPoultryLocations();
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(),
});
debounce(baseLogic.searchValue, (callback) {
if (callback != null &&
callback.trim().isNotEmpty &&
searchedPoultryLocation.value.status != ResourceStatus.loading) {
searchPoultryLocations();
}
}, time: Duration(seconds: 2));
}
@override
@@ -92,7 +66,6 @@ class InspectionMapLogic extends GetxController with GetTickerProviderStateMixin
@override
void onClose() {
slidController.close();
super.onClose();
}
@@ -132,9 +105,9 @@ class InspectionMapLogic extends GetxController with GetTickerProviderStateMixin
center.longitude,
radius * 1000,
);
markers2.addAll(filtered);
final existingIds = markers2.map((e) => e.id).toSet();
final uniqueFiltered = filtered.where((e) => !existingIds.contains(e.id)).toList();
markers2.addAll(uniqueFiltered);
});
}
@@ -154,9 +127,9 @@ class InspectionMapLogic extends GetxController with GetTickerProviderStateMixin
Future<void> triggerSlidableAnimation() async {
await Future.delayed(Duration(milliseconds: 200));
await slidController.value.openEndActionPane();
//await slidController.value.openEndActionPane();
await Future.delayed(Duration(milliseconds: 200));
await slidController.value.close();
//await slidController.value.close();
showSlideHint = false;
}
@@ -184,6 +157,25 @@ class InspectionMapLogic extends GetxController with GetTickerProviderStateMixin
);
}
Future<void> searchPoultryLocations() async {
searchedPoultryLocation.value = Resource<List<PoultryLocationModel>>.loading();
await safeCall(
call: () => inspectionRepository.getNearbyLocation(value: baseLogic.searchValue.value),
onSuccess: (result) {
if (result != null || result!.isNotEmpty) {
searchedPoultryLocation.value = Resource<List<PoultryLocationModel>>.success(result);
} else {
searchedPoultryLocation.value = Resource<List<PoultryLocationModel>>.empty();
}
},
onError: (error, stackTrace) {
searchedPoultryLocation.value = Resource<List<PoultryLocationModel>>.error(
error.toString(),
);
},
);
}
double getVisibleRadiusKm({
required double zoom,
required double screenWidthPx,

View File

@@ -4,7 +4,6 @@ import 'package:rasadyar_inspection/data/model/response/poultry_location/poultry
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/custom_chips.dart';
import 'package:rasadyar_inspection/presentation/widget/search.dart';
import 'logic.dart';
@@ -87,7 +86,24 @@ class InspectionMapPage extends GetView<InspectionMapLogic> {
);
}, controller.currentLocation),
Positioned(
Obx(() {
if (controller.baseLogic.isSearchSelected.value) {
WidgetsBinding.instance.addPostFrameCallback((_) {
if (Get.isBottomSheetOpen != true) {
Get.bottomSheet(
searchWidget(),
isDismissible: true,
ignoreSafeArea: false,
isScrollControlled: true,
);
}
});
}
return const SizedBox.shrink();
}),
// Uncomment the following lines to enable the search widget
/* Positioned(
top: 10,
left: 20,
right: 20,
@@ -102,6 +118,126 @@ class InspectionMapPage extends GetView<InspectionMapLogic> {
return SizedBox.shrink();
}
}, controller.baseLogic.isSearchSelected),
),*/
],
),
);
}
BaseBottomSheet searchWidget() {
return BaseBottomSheet(
height: Get.height * 0.85,
rootChild: Column(
spacing: 8,
children: [
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.baseLogic.searchTextController.clear();
controller.baseLogic.searchValue.value = null;
controller.baseLogic.isSearchSelected.value = false;
controller.searchedPoultryLocation.value = Resource.initial();
},
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.baseLogic.searchValue,
),
hintText: 'جستجو کنید ...',
hintStyle: AppFonts.yekan16.copyWith(color: AppColor.blueNormal),
filledColor: Colors.white,
filled: true,
controller: controller.baseLogic.searchTextController,
onChanged: (val) => controller.baseLogic.searchValue.value = val,
),
Expanded(
child: ObxValue((rxData) {
final resource = rxData.value;
final status = resource.status;
final items = resource.data;
final message = resource.message ?? 'خطا در بارگذاری';
if (status == ResourceStatus.initial) {
return Center(child: Text('ابتدا جستجو کنید'));
}
if (status == ResourceStatus.loading) {
return const Center(child: CircularProgressIndicator());
}
if (status == ResourceStatus.error) {
return Center(child: Text(message));
}
if (items == null || items.isEmpty) {
return Center(child: EmptyWidget());
}
return ListView.separated(
itemCount: items.length,
separatorBuilder: (context, index) => SizedBox(height: 8),
itemBuilder: (context, index) {
final item = items[index]; // اگر item استفاده نمیشه، می‌تونه حذف بشه
return ListItem2(
index: index,
labelColor: AppColor.blueLight,
labelIcon: Assets.vec.cowSvg.path,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
item.unitName ?? 'N/A',
style: AppFonts.yekan10.copyWith(color: AppColor.blueNormal),
),
Text(
item.user?.fullname ?? '',
style: AppFonts.yekan12.copyWith(color: AppColor.darkGreyDarkHover),
),
],
),
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'جوجه ریزی فعال',
style: AppFonts.yekan10.copyWith(color: AppColor.blueNormal),
),
Text(
(item.hatching != null && item.hatching!.isNotEmpty)
? 'دارد'
: 'ندراد',
style: AppFonts.yekan12.copyWith(color: AppColor.darkGreyDarkHover),
),
],
),
],
),
);
},
);
}, controller.searchedPoultryLocation),
),
],
),
@@ -315,221 +451,310 @@ class InspectionMapPage extends GetView<InspectionMapLogic> {
point: point,
width: isZoomedIn && isVisible ? 180.w : 40.h,
height: isZoomedIn && isVisible ? 50.h : 50.h,
child: isZoomedIn && isVisible
? GestureDetector(
onTap: () {
Get.bottomSheet(
ObxValue((data) {
return BaseBottomSheet(
height: data.value ? 450.h : 150.h,
child: ListItemWithOutCounter(
secondChild: Column(
spacing: 8,
children: [
Padding(
padding: EdgeInsets.symmetric(horizontal: 12.w),
child: Column(
spacing: 8,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.center,
child: GestureDetector(
onTap: () {
bool hasHatching = location.hatching != null && location.hatching!.isNotEmpty;
Get.bottomSheet(
ObxValue((data) {
return BaseBottomSheet(
height: data.value
? hasHatching
? 550.h
: 400.h
: 150.h,
child: Column(
spacing: 12,
children: [
ListItemWithOutCounter(
secondChild: Column(
spacing: 8,
children: [
Padding(
padding: EdgeInsets.symmetric(horizontal: 12.w),
child: Column(
spacing: 8,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
location.unitName ?? 'N/A',
textAlign: TextAlign.center,
style: AppFonts.yekan16.copyWith(color: AppColor.greenDark),
),
],
),
Container(
height: 32.h,
padding: EdgeInsets.symmetric(horizontal: 8),
decoration: ShapeDecoration(
color: AppColor.blueLight,
shape: RoundedRectangleBorder(
side: BorderSide(
width: 1.w,
color: AppColor.blueLightHover,
),
borderRadius: BorderRadius.circular(8),
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
spacing: 3,
children: [
Text(
'داوود خرم پور',
textAlign: TextAlign.center,
style: AppFonts.yekan16.copyWith(
color: AppColor.greenDark,
'جوجه ریزی فعال',
style: AppFonts.yekan14.copyWith(
color: AppColor.textColor,
),
),
Text(
hasHatching ? 'دارد' : 'ندارد',
style: AppFonts.yekan14.copyWith(
color: AppColor.blueNormal,
),
),
],
),
Container(
height: 32.h,
padding: EdgeInsets.symmetric(horizontal: 8),
decoration: ShapeDecoration(
color: AppColor.blueLight,
shape: RoundedRectangleBorder(
side: BorderSide(
width: 1.w,
color: AppColor.blueLightHover,
),
buildRow(
title: 'مشخصات خریدار',
value: location.user?.fullname ?? 'N/A',
),
buildRow(
title: 'تلفن خریدار',
value: location.user?.mobile ?? 'N/A',
valueStyle: AppFonts.yekan14.copyWith(
color: AppColor.blueNormal,
),
),
Visibility(
visible: location.address?.city?.name != null,
child: buildRow(
title: 'شهر',
value: location.address?.city?.name ?? 'N/A',
),
),
Visibility(
visible: location.address?.address != null,
child: buildRow(
title: 'آردس',
value: location.address?.address ?? 'N/A',
),
),
buildRow(
title: 'شناسه یکتا',
value: location.breedingUniqueId ?? 'N/A',
),
],
),
),
Row(
children: [
Expanded(
child: Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
spacing: 7,
children: [
RElevated(
width: 40.h,
height: 38.h,
backgroundColor: AppColor.greenNormal,
child: Assets.vec.messageAddSvg.svg(
width: 24.w,
height: 24.h,
colorFilter: ColorFilter.mode(
Colors.white,
BlendMode.srcIn,
),
borderRadius: BorderRadius.circular(8),
),
onPressed: () {},
),
RElevated(
width: 150.w,
height: 40.h,
backgroundColor: AppColor.blueNormal,
onPressed: () {
/* controller.setEditData(item);
Get.bottomSheet(
addOrEditBottomSheet(true),
isScrollControlled: true,
backgroundColor: Colors.transparent,
).whenComplete(() {});*/
},
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
spacing: 8,
children: [
Assets.vec.mapSvg.svg(
width: 24.w,
height: 24.h,
colorFilter: ColorFilter.mode(
Colors.white,
BlendMode.srcIn,
),
),
Text(
'جزییات کامل',
style: AppFonts.yekan14Bold.copyWith(
color: Colors.white,
),
),
],
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
spacing: 3,
children: [
Text(
'تاریخ بازرسی',
style: AppFonts.yekan14.copyWith(
color: AppColor.textColor,
ROutlinedElevated(
width: 150.w,
height: 40.h,
onPressed: () {
buildDeleteDialog(
onConfirm: () async {},
onRefresh: () async {},
);
},
borderColor: AppColor.bgIcon,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
spacing: 8,
children: [
Assets.vec.securityTimeSvg.svg(
width: 24.w,
height: 24.h,
colorFilter: ColorFilter.mode(
AppColor.bgIcon,
BlendMode.srcIn,
),
),
),
Text(
'1403/12/12',
style: AppFonts.yekan14.copyWith(
color: AppColor.blueNormal,
Text(
'سوابق بازرسی',
style: AppFonts.yekan14Bold.copyWith(
color: AppColor.bgIcon,
),
),
),
],
],
),
),
],
),
),
],
),
],
),
labelColor: AppColor.blueLight,
labelIcon: Assets.vec.cowSvg.path,
labelIconColor: AppColor.bgIcon,
onTap: () => data.value = !data.value,
selected: data.value,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
location.unitName ?? 'N/A',
style: AppFonts.yekan10.copyWith(color: AppColor.blueNormal),
),
Text(
location.user?.fullname ?? '',
style: AppFonts.yekan12.copyWith(
color: AppColor.darkGreyDarkHover,
),
),
],
),
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'جوجه ریزی فعال',
style: AppFonts.yekan10.copyWith(color: AppColor.blueNormal),
),
Text(
(location.hatching != null && location.hatching!.isNotEmpty)
? 'دارد'
: 'ندراد',
style: AppFonts.yekan12.copyWith(
color: AppColor.darkGreyDarkHover,
),
),
],
),
Assets.vec.scanBarcodeSvg.svg(),
],
),
),
Visibility(
visible: hasHatching,
child: Container(
width: Get.width,
margin: const EdgeInsets.fromLTRB(0, 0, 10, 0),
padding: EdgeInsets.all(8.r),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8),
border: Border.all(width: 1, color: AppColor.lightGreyNormalHover),
),
child: Column(
spacing: 8.h,
children: [
Container(
height: 32.h,
padding: EdgeInsets.symmetric(horizontal: 8),
decoration: ShapeDecoration(
color: AppColor.blueLight,
shape: RoundedRectangleBorder(
side: BorderSide(width: 1.w, color: AppColor.blueLightHover),
borderRadius: BorderRadius.circular(8),
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
spacing: 3,
children: [
Text(
'تاریخ',
style: AppFonts.yekan14.copyWith(color: AppColor.textColor),
),
buildRow(
title: 'تلفن خریدار',
value: '0326598653',
valueStyle: AppFonts.yekan14.copyWith(
color: AppColor.blueNormal,
),
Text(
location.hatching?.first.date?.formattedJalaliDate ?? 'N/A',
style: AppFonts.yekan14.copyWith(color: AppColor.blueNormal),
),
buildRow(title: 'آخرین فعالیت', value: '1409/12/12'),
buildRow(title: 'موجودی', value: '5KG'),
buildRow(title: 'فروش رفته', value: '5KG'),
],
),
),
Row(
children: [
Expanded(
child: Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
spacing: 7,
children: [
RElevated(
width: 40.h,
height: 38.h,
backgroundColor: AppColor.greenNormal,
child: Assets.vec.messageAddSvg.svg(
width: 24.w,
height: 24.h,
colorFilter: ColorFilter.mode(
Colors.white,
BlendMode.srcIn,
),
),
onPressed: () {},
),
RElevated(
width: 150.w,
height: 40.h,
backgroundColor: AppColor.blueNormal,
onPressed: () {
/* controller.setEditData(item);
Get.bottomSheet(
addOrEditBottomSheet(true),
isScrollControlled: true,
backgroundColor: Colors.transparent,
).whenComplete(() {});*/
},
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
spacing: 8,
children: [
Assets.vec.mapSvg.svg(
width: 24.w,
height: 24.h,
colorFilter: ColorFilter.mode(
Colors.white,
BlendMode.srcIn,
),
),
Text(
'مسیریابی',
style: AppFonts.yekan14Bold.copyWith(
color: Colors.white,
),
),
],
),
),
ROutlinedElevated(
width: 150.w,
height: 40.h,
onPressed: () {
buildDeleteDialog(
onConfirm: () async {},
onRefresh: () async {},
);
},
borderColor: AppColor.bgIcon,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
spacing: 8,
children: [
Assets.vec.securityTimeSvg.svg(
width: 24.w,
height: 24.h,
colorFilter: ColorFilter.mode(
AppColor.bgIcon,
BlendMode.srcIn,
),
),
Text(
'سوابق بازرسی',
style: AppFonts.yekan14Bold.copyWith(
color: AppColor.bgIcon,
),
),
],
),
),
],
),
),
],
buildRow(
title: 'باقیمانده',
value: location.hatching?.first.leftOver.separatedByComma ?? 'N/A',
),
],
),
labelColor: AppColor.blueLight,
labelIcon: Assets.vec.cowSvg.path,
labelIconColor: AppColor.bgIcon,
onTap: () => data.value = !data.value,
selected: data.value,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'داود خرم مهری پور',
style: AppFonts.yekan10.copyWith(color: AppColor.blueNormal),
),
Text(
'گوشت و مرغ',
style: AppFonts.yekan12.copyWith(
color: AppColor.darkGreyDarkHover,
),
),
],
buildRow(
title: 'سن جوجه ریزی',
value: '${location.hatching?.first.chickenAge ?? 'N/A'} روز',
),
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'باقی مانده',
style: AppFonts.yekan10.copyWith(color: AppColor.blueNormal),
),
Text(
'0 کیلوگرم',
style: AppFonts.yekan12.copyWith(
color: AppColor.darkGreyDarkHover,
),
),
],
buildRow(
title: 'شماره مجوز جوجه ریزی',
value: location.hatching?.first.licenceNumber.toString() ?? 'N/A',
),
Assets.vec.scanBarcodeSvg.svg(),
],
),
),
);
}, controller.isSelectedDetailsLocation),
);
},
child: Container(
),
],
),
);
}, controller.isSelectedDetailsLocation),
isScrollControlled: true,
isDismissible: true,
);
},
child: isZoomedIn && isVisible
? Container(
height: 30.h,
padding: EdgeInsets.all(5.r),
decoration: BoxDecoration(
@@ -551,9 +776,9 @@ class InspectionMapPage extends GetView<InspectionMapLogic> {
Text(location.user?.fullname ?? '', style: AppFonts.yekan12),
],
),
),
)
: Assets.vec.chickenMapMarkerSvg.svg(width: 24.w, height: 24.h),
)
: Assets.vec.chickenMapMarkerSvg.svg(width: 24.w, height: 24.h),
),
);
}).toList();

View File

@@ -29,7 +29,7 @@ class RecordsPage extends GetView<RecordsLogic> {
itemBuilder: (context, index) {
var item = data.value.data!.results![index];
return ObxValue((val) {
return ListItem2(
return ExpandableListItem2(
selected: val.value == index,
onTap: () => controller.toggleExpandedList(index),
index: index,

View File

@@ -29,7 +29,7 @@ class UsersPage extends GetView<UsersLogic> {
itemBuilder: (context, index) {
var item = data.value.data!.results![index];
return ObxValue((val) {
return ListItem2(
return ExpandableListItem2(
selected: val.value == index,
onTap: () => controller.toggleExpandedList(index),
index: index,