diff --git a/packages/inspection/lib/data/model/response/poultry_location/poultry_location_model.dart b/packages/inspection/lib/data/model/response/poultry_location/poultry_location_model.dart index 330cc53..a0da0d1 100644 --- a/packages/inspection/lib/data/model/response/poultry_location/poultry_location_model.dart +++ b/packages/inspection/lib/data/model/response/poultry_location/poultry_location_model.dart @@ -1,4 +1,4 @@ -import 'package:freezed_annotation/freezed_annotation.dart'; +import 'package:rasadyar_core/core.dart'; part 'poultry_location_model.freezed.dart'; part 'poultry_location_model.g.dart'; @@ -8,61 +8,48 @@ abstract class PoultryLocationModel with _$PoultryLocationModel { const factory PoultryLocationModel({ int? id, String? unitName, - @JsonKey(name: 'Lat') - double? lat, - @JsonKey(name: 'Long') - double? long, + @JsonKey(name: 'Lat') double? lat, + @JsonKey(name: 'Long') double? long, User? user, List? hatching, Address? address, String? breedingUniqueId, + @JsonKey(includeFromJson: false, includeToJson: false) LatLng? latLng, }) = _PoultryLocationModel; factory PoultryLocationModel.fromJson(Map json) => - _$PoultryLocationModelFromJson(json); + _$PoultryLocationModelFromJson(json).copyWith( + latLng: (json['Lat'] != null && json['Long'] != null) + ? LatLng(json['Lat'] as double, json['Long'] as double) + : null, + ); } @freezed abstract class User with _$User { - const factory User({ - String? fullname, - String? mobile, - }) = _User; + const factory User({String? fullname, String? mobile}) = _User; factory User.fromJson(Map json) => _$UserFromJson(json); } @freezed abstract class Hatching with _$Hatching { - const factory Hatching({ + const factory Hatching({int? leftOver, int? chickenAge, DateTime? date, String? licenceNumber}) = + _Hatching; - int? leftOver, - int? chickenAge, - DateTime? date, - String? licenceNumber, - - }) = _Hatching; - - factory Hatching.fromJson(Map json) => - _$HatchingFromJson(json); + factory Hatching.fromJson(Map json) => _$HatchingFromJson(json); } @freezed abstract class Address with _$Address { - const factory Address({ - City? city, - String? address, - }) = _Address; + const factory Address({City? city, String? address}) = _Address; - factory Address.fromJson(Map json) => - _$AddressFromJson(json); + factory Address.fromJson(Map json) => _$AddressFromJson(json); } @freezed abstract class City with _$City { - const factory City({ - String? name, - }) = _City; + const factory City({String? name}) = _City; factory City.fromJson(Map json) => _$CityFromJson(json); } diff --git a/packages/inspection/lib/data/model/response/poultry_location/poultry_location_model.freezed.dart b/packages/inspection/lib/data/model/response/poultry_location/poultry_location_model.freezed.dart index 98cf153..68f16d3 100644 --- a/packages/inspection/lib/data/model/response/poultry_location/poultry_location_model.freezed.dart +++ b/packages/inspection/lib/data/model/response/poultry_location/poultry_location_model.freezed.dart @@ -15,7 +15,7 @@ T _$identity(T value) => value; /// @nodoc mixin _$PoultryLocationModel { - int? get id; String? get unitName;@JsonKey(name: 'Lat') double? get lat;@JsonKey(name: 'Long') double? get long; User? get user; List? get hatching; Address? get address; String? get breedingUniqueId; + int? get id; String? get unitName;@JsonKey(name: 'Lat') double? get lat;@JsonKey(name: 'Long') double? get long; User? get user; List? get hatching; Address? get address; String? get breedingUniqueId;@JsonKey(includeFromJson: false, includeToJson: false) LatLng? get latLng; /// Create a copy of PoultryLocationModel /// with the given fields replaced by the non-null parameter values. @JsonKey(includeFromJson: false, includeToJson: false) @@ -28,16 +28,16 @@ $PoultryLocationModelCopyWith get copyWith => _$PoultryLoc @override bool operator ==(Object other) { - return identical(this, other) || (other.runtimeType == runtimeType&&other is PoultryLocationModel&&(identical(other.id, id) || other.id == id)&&(identical(other.unitName, unitName) || other.unitName == unitName)&&(identical(other.lat, lat) || other.lat == lat)&&(identical(other.long, long) || other.long == long)&&(identical(other.user, user) || other.user == user)&&const DeepCollectionEquality().equals(other.hatching, hatching)&&(identical(other.address, address) || other.address == address)&&(identical(other.breedingUniqueId, breedingUniqueId) || other.breedingUniqueId == breedingUniqueId)); + return identical(this, other) || (other.runtimeType == runtimeType&&other is PoultryLocationModel&&(identical(other.id, id) || other.id == id)&&(identical(other.unitName, unitName) || other.unitName == unitName)&&(identical(other.lat, lat) || other.lat == lat)&&(identical(other.long, long) || other.long == long)&&(identical(other.user, user) || other.user == user)&&const DeepCollectionEquality().equals(other.hatching, hatching)&&(identical(other.address, address) || other.address == address)&&(identical(other.breedingUniqueId, breedingUniqueId) || other.breedingUniqueId == breedingUniqueId)&&(identical(other.latLng, latLng) || other.latLng == latLng)); } @JsonKey(includeFromJson: false, includeToJson: false) @override -int get hashCode => Object.hash(runtimeType,id,unitName,lat,long,user,const DeepCollectionEquality().hash(hatching),address,breedingUniqueId); +int get hashCode => Object.hash(runtimeType,id,unitName,lat,long,user,const DeepCollectionEquality().hash(hatching),address,breedingUniqueId,latLng); @override String toString() { - return 'PoultryLocationModel(id: $id, unitName: $unitName, lat: $lat, long: $long, user: $user, hatching: $hatching, address: $address, breedingUniqueId: $breedingUniqueId)'; + return 'PoultryLocationModel(id: $id, unitName: $unitName, lat: $lat, long: $long, user: $user, hatching: $hatching, address: $address, breedingUniqueId: $breedingUniqueId, latLng: $latLng)'; } @@ -48,7 +48,7 @@ abstract mixin class $PoultryLocationModelCopyWith<$Res> { factory $PoultryLocationModelCopyWith(PoultryLocationModel value, $Res Function(PoultryLocationModel) _then) = _$PoultryLocationModelCopyWithImpl; @useResult $Res call({ - int? id, String? unitName,@JsonKey(name: 'Lat') double? lat,@JsonKey(name: 'Long') double? long, User? user, List? hatching, Address? address, String? breedingUniqueId + int? id, String? unitName,@JsonKey(name: 'Lat') double? lat,@JsonKey(name: 'Long') double? long, User? user, List? hatching, Address? address, String? breedingUniqueId,@JsonKey(includeFromJson: false, includeToJson: false) LatLng? latLng }); @@ -65,7 +65,7 @@ class _$PoultryLocationModelCopyWithImpl<$Res> /// Create a copy of PoultryLocationModel /// with the given fields replaced by the non-null parameter values. -@pragma('vm:prefer-inline') @override $Res call({Object? id = freezed,Object? unitName = freezed,Object? lat = freezed,Object? long = freezed,Object? user = freezed,Object? hatching = freezed,Object? address = freezed,Object? breedingUniqueId = freezed,}) { +@pragma('vm:prefer-inline') @override $Res call({Object? id = freezed,Object? unitName = freezed,Object? lat = freezed,Object? long = freezed,Object? user = freezed,Object? hatching = freezed,Object? address = freezed,Object? breedingUniqueId = freezed,Object? latLng = freezed,}) { return _then(_self.copyWith( id: freezed == id ? _self.id : id // ignore: cast_nullable_to_non_nullable as int?,unitName: freezed == unitName ? _self.unitName : unitName // ignore: cast_nullable_to_non_nullable @@ -75,7 +75,8 @@ as double?,user: freezed == user ? _self.user : user // ignore: cast_nullable_to as User?,hatching: freezed == hatching ? _self.hatching : hatching // ignore: cast_nullable_to_non_nullable as List?,address: freezed == address ? _self.address : address // ignore: cast_nullable_to_non_nullable as Address?,breedingUniqueId: freezed == breedingUniqueId ? _self.breedingUniqueId : breedingUniqueId // ignore: cast_nullable_to_non_nullable -as String?, +as String?,latLng: freezed == latLng ? _self.latLng : latLng // ignore: cast_nullable_to_non_nullable +as LatLng?, )); } /// Create a copy of PoultryLocationModel @@ -184,10 +185,10 @@ return $default(_that);case _: /// } /// ``` -@optionalTypeArgs TResult maybeWhen(TResult Function( int? id, String? unitName, @JsonKey(name: 'Lat') double? lat, @JsonKey(name: 'Long') double? long, User? user, List? hatching, Address? address, String? breedingUniqueId)? $default,{required TResult orElse(),}) {final _that = this; +@optionalTypeArgs TResult maybeWhen(TResult Function( int? id, String? unitName, @JsonKey(name: 'Lat') double? lat, @JsonKey(name: 'Long') double? long, User? user, List? hatching, Address? address, String? breedingUniqueId, @JsonKey(includeFromJson: false, includeToJson: false) LatLng? latLng)? $default,{required TResult orElse(),}) {final _that = this; switch (_that) { case _PoultryLocationModel() when $default != null: -return $default(_that.id,_that.unitName,_that.lat,_that.long,_that.user,_that.hatching,_that.address,_that.breedingUniqueId);case _: +return $default(_that.id,_that.unitName,_that.lat,_that.long,_that.user,_that.hatching,_that.address,_that.breedingUniqueId,_that.latLng);case _: return orElse(); } @@ -205,10 +206,10 @@ return $default(_that.id,_that.unitName,_that.lat,_that.long,_that.user,_that.ha /// } /// ``` -@optionalTypeArgs TResult when(TResult Function( int? id, String? unitName, @JsonKey(name: 'Lat') double? lat, @JsonKey(name: 'Long') double? long, User? user, List? hatching, Address? address, String? breedingUniqueId) $default,) {final _that = this; +@optionalTypeArgs TResult when(TResult Function( int? id, String? unitName, @JsonKey(name: 'Lat') double? lat, @JsonKey(name: 'Long') double? long, User? user, List? hatching, Address? address, String? breedingUniqueId, @JsonKey(includeFromJson: false, includeToJson: false) LatLng? latLng) $default,) {final _that = this; switch (_that) { case _PoultryLocationModel(): -return $default(_that.id,_that.unitName,_that.lat,_that.long,_that.user,_that.hatching,_that.address,_that.breedingUniqueId);case _: +return $default(_that.id,_that.unitName,_that.lat,_that.long,_that.user,_that.hatching,_that.address,_that.breedingUniqueId,_that.latLng);case _: throw StateError('Unexpected subclass'); } @@ -225,10 +226,10 @@ return $default(_that.id,_that.unitName,_that.lat,_that.long,_that.user,_that.ha /// } /// ``` -@optionalTypeArgs TResult? whenOrNull(TResult? Function( int? id, String? unitName, @JsonKey(name: 'Lat') double? lat, @JsonKey(name: 'Long') double? long, User? user, List? hatching, Address? address, String? breedingUniqueId)? $default,) {final _that = this; +@optionalTypeArgs TResult? whenOrNull(TResult? Function( int? id, String? unitName, @JsonKey(name: 'Lat') double? lat, @JsonKey(name: 'Long') double? long, User? user, List? hatching, Address? address, String? breedingUniqueId, @JsonKey(includeFromJson: false, includeToJson: false) LatLng? latLng)? $default,) {final _that = this; switch (_that) { case _PoultryLocationModel() when $default != null: -return $default(_that.id,_that.unitName,_that.lat,_that.long,_that.user,_that.hatching,_that.address,_that.breedingUniqueId);case _: +return $default(_that.id,_that.unitName,_that.lat,_that.long,_that.user,_that.hatching,_that.address,_that.breedingUniqueId,_that.latLng);case _: return null; } @@ -240,7 +241,7 @@ return $default(_that.id,_that.unitName,_that.lat,_that.long,_that.user,_that.ha @JsonSerializable() class _PoultryLocationModel implements PoultryLocationModel { - const _PoultryLocationModel({this.id, this.unitName, @JsonKey(name: 'Lat') this.lat, @JsonKey(name: 'Long') this.long, this.user, final List? hatching, this.address, this.breedingUniqueId}): _hatching = hatching; + const _PoultryLocationModel({this.id, this.unitName, @JsonKey(name: 'Lat') this.lat, @JsonKey(name: 'Long') this.long, this.user, final List? hatching, this.address, this.breedingUniqueId, @JsonKey(includeFromJson: false, includeToJson: false) this.latLng}): _hatching = hatching; factory _PoultryLocationModel.fromJson(Map json) => _$PoultryLocationModelFromJson(json); @override final int? id; @@ -259,6 +260,7 @@ class _PoultryLocationModel implements PoultryLocationModel { @override final Address? address; @override final String? breedingUniqueId; +@override@JsonKey(includeFromJson: false, includeToJson: false) final LatLng? latLng; /// Create a copy of PoultryLocationModel /// with the given fields replaced by the non-null parameter values. @@ -273,16 +275,16 @@ Map toJson() { @override bool operator ==(Object other) { - return identical(this, other) || (other.runtimeType == runtimeType&&other is _PoultryLocationModel&&(identical(other.id, id) || other.id == id)&&(identical(other.unitName, unitName) || other.unitName == unitName)&&(identical(other.lat, lat) || other.lat == lat)&&(identical(other.long, long) || other.long == long)&&(identical(other.user, user) || other.user == user)&&const DeepCollectionEquality().equals(other._hatching, _hatching)&&(identical(other.address, address) || other.address == address)&&(identical(other.breedingUniqueId, breedingUniqueId) || other.breedingUniqueId == breedingUniqueId)); + return identical(this, other) || (other.runtimeType == runtimeType&&other is _PoultryLocationModel&&(identical(other.id, id) || other.id == id)&&(identical(other.unitName, unitName) || other.unitName == unitName)&&(identical(other.lat, lat) || other.lat == lat)&&(identical(other.long, long) || other.long == long)&&(identical(other.user, user) || other.user == user)&&const DeepCollectionEquality().equals(other._hatching, _hatching)&&(identical(other.address, address) || other.address == address)&&(identical(other.breedingUniqueId, breedingUniqueId) || other.breedingUniqueId == breedingUniqueId)&&(identical(other.latLng, latLng) || other.latLng == latLng)); } @JsonKey(includeFromJson: false, includeToJson: false) @override -int get hashCode => Object.hash(runtimeType,id,unitName,lat,long,user,const DeepCollectionEquality().hash(_hatching),address,breedingUniqueId); +int get hashCode => Object.hash(runtimeType,id,unitName,lat,long,user,const DeepCollectionEquality().hash(_hatching),address,breedingUniqueId,latLng); @override String toString() { - return 'PoultryLocationModel(id: $id, unitName: $unitName, lat: $lat, long: $long, user: $user, hatching: $hatching, address: $address, breedingUniqueId: $breedingUniqueId)'; + return 'PoultryLocationModel(id: $id, unitName: $unitName, lat: $lat, long: $long, user: $user, hatching: $hatching, address: $address, breedingUniqueId: $breedingUniqueId, latLng: $latLng)'; } @@ -293,7 +295,7 @@ abstract mixin class _$PoultryLocationModelCopyWith<$Res> implements $PoultryLoc factory _$PoultryLocationModelCopyWith(_PoultryLocationModel value, $Res Function(_PoultryLocationModel) _then) = __$PoultryLocationModelCopyWithImpl; @override @useResult $Res call({ - int? id, String? unitName,@JsonKey(name: 'Lat') double? lat,@JsonKey(name: 'Long') double? long, User? user, List? hatching, Address? address, String? breedingUniqueId + int? id, String? unitName,@JsonKey(name: 'Lat') double? lat,@JsonKey(name: 'Long') double? long, User? user, List? hatching, Address? address, String? breedingUniqueId,@JsonKey(includeFromJson: false, includeToJson: false) LatLng? latLng }); @@ -310,7 +312,7 @@ class __$PoultryLocationModelCopyWithImpl<$Res> /// Create a copy of PoultryLocationModel /// with the given fields replaced by the non-null parameter values. -@override @pragma('vm:prefer-inline') $Res call({Object? id = freezed,Object? unitName = freezed,Object? lat = freezed,Object? long = freezed,Object? user = freezed,Object? hatching = freezed,Object? address = freezed,Object? breedingUniqueId = freezed,}) { +@override @pragma('vm:prefer-inline') $Res call({Object? id = freezed,Object? unitName = freezed,Object? lat = freezed,Object? long = freezed,Object? user = freezed,Object? hatching = freezed,Object? address = freezed,Object? breedingUniqueId = freezed,Object? latLng = freezed,}) { return _then(_PoultryLocationModel( id: freezed == id ? _self.id : id // ignore: cast_nullable_to_non_nullable as int?,unitName: freezed == unitName ? _self.unitName : unitName // ignore: cast_nullable_to_non_nullable @@ -320,7 +322,8 @@ as double?,user: freezed == user ? _self.user : user // ignore: cast_nullable_to as User?,hatching: freezed == hatching ? _self._hatching : hatching // ignore: cast_nullable_to_non_nullable as List?,address: freezed == address ? _self.address : address // ignore: cast_nullable_to_non_nullable as Address?,breedingUniqueId: freezed == breedingUniqueId ? _self.breedingUniqueId : breedingUniqueId // ignore: cast_nullable_to_non_nullable -as String?, +as String?,latLng: freezed == latLng ? _self.latLng : latLng // ignore: cast_nullable_to_non_nullable +as LatLng?, )); } diff --git a/packages/inspection/lib/presentation/pages/inspection_map/logic.dart b/packages/inspection/lib/presentation/pages/inspection_map/logic.dart index e522219..04ef033 100644 --- a/packages/inspection/lib/presentation/pages/inspection_map/logic.dart +++ b/packages/inspection/lib/presentation/pages/inspection_map/logic.dart @@ -1,20 +1,18 @@ import 'dart:async'; -import 'dart:math'; -import 'package:flutter/material.dart'; import 'package:rasadyar_core/core.dart'; import 'package:rasadyar_inspection/data/model/response/poultry_location/poultry_location_model.dart'; import 'package:rasadyar_inspection/data/repositories/inspection/inspection_repository_imp.dart'; import 'package:rasadyar_inspection/injection/inspection_di.dart'; import 'package:rasadyar_inspection/presentation/widget/base_page/logic.dart'; -class InspectionMapLogic extends GetxController with GetTickerProviderStateMixin { +import 'widget/map/logic.dart'; + +class InspectionMapLogic extends GetxController { final BaseLogic baseLogic = Get.find(); - + final MapLogic mapLogic = Get.find(); InspectionRepositoryImp inspectionRepository = diInspection.get(); - final distance = Distance(); - Rx currentLocation = LatLng(34.798315281272544, 48.51479142983491).obs; Rx>> allPoultryLocation = @@ -26,26 +24,12 @@ class InspectionMapLogic extends GetxController with GetTickerProviderStateMixin RxList markers = [].obs; RxList markers2 = [].obs; - Timer? _debounceTimer; - RxBool isLoading = false.obs; - RxBool isSelectedDetailsLocation = false.obs; - RxInt filterIndex = 0.obs; RxInt showIndex = 0.obs; - bool showSlideHint = true; - RxInt currentZoom = 15.obs; - Rx mapController = MapController().obs; - late final AnimatedMapController animatedMapController; @override void onInit() { super.onInit(); - animatedMapController = AnimatedMapController( - vsync: this, - duration: const Duration(milliseconds: 500), - curve: Curves.easeInOut, - cancelPreviousAnimations: true, - ); fetchAllPoultryLocations(); @@ -69,82 +53,14 @@ class InspectionMapLogic extends GetxController with GetTickerProviderStateMixin super.onClose(); } - Future 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(PoultryLocationModel( - lat: latLng.latitude, - long: latLng.longitude - ));*/ - animatedMapController.animateTo( - dest: latLng, - zoom: 18, - curve: Curves.easeInOut, - duration: const Duration(seconds: 1), - ); - isLoading.value = false; - } - - void debouncedUpdateVisibleMarkers({required LatLng center, required double zoom}) { - _debounceTimer?.cancel(); - _debounceTimer = Timer(const Duration(milliseconds: 300), () { - final radius = getVisibleRadiusKm( - zoom: zoom, - screenWidthPx: Get.width.toDouble(), - latitude: center.latitude, - ); - - final filtered = filterNearbyMarkers( - allPoultryLocation.value.data ?? [], - center.latitude, - center.longitude, - radius * 1000, - ); - final existingIds = markers2.map((e) => e.id).toSet(); - final uniqueFiltered = filtered.where((e) => !existingIds.contains(e.id)).toList(); - markers2.addAll(uniqueFiltered); - }); - } - - List filterNearbyMarkers( - List allMarkers, - double centerLat, - double centerLng, - double radiusInMeters, - ) { - final center = LatLng(centerLat, centerLng); - - return allMarkers.where((marker) { - var tmp = LatLng(marker.lat ?? 0, marker.long ?? 0); - return distance(center, tmp) <= radiusInMeters; - }).toList(); - } - - Future triggerSlidableAnimation() async { - await Future.delayed(Duration(milliseconds: 200)); - //await slidController.value.openEndActionPane(); - await Future.delayed(Duration(milliseconds: 200)); - //await slidController.value.close(); - showSlideHint = false; - } - Future fetchAllPoultryLocations() async { - isLoading.value = true; allPoultryLocation.value = Resource>.loading(); await safeCall( - call: () => inspectionRepository.getNearbyLocation( - centerLat: currentLocation.value.latitude, - centerLng: currentLocation.value.longitude, - radius: 15, // Radius in K meters - ), + call: () => inspectionRepository.getNearbyLocation(), onSuccess: (result) { if (result != null) { allPoultryLocation.value = Resource>.success(result); + mapLogic.allLocations.value = Resource>.success(result); } else { allPoultryLocation.value = Resource>.error( 'No locations found', @@ -164,8 +80,11 @@ class InspectionMapLogic extends GetxController with GetTickerProviderStateMixin onSuccess: (result) { if (result != null || result!.isNotEmpty) { searchedPoultryLocation.value = Resource>.success(result); + mapLogic.hasFilterOrSearch.value = true; + mapLogic.filteredLocations.value = Resource>.success(result); } else { searchedPoultryLocation.value = Resource>.empty(); + mapLogic.filteredLocations.value = Resource>.empty(); } }, onError: (error, stackTrace) { @@ -175,14 +94,4 @@ class InspectionMapLogic extends GetxController with GetTickerProviderStateMixin }, ); } - - double getVisibleRadiusKm({ - required double zoom, - required double screenWidthPx, - required double latitude, - }) { - double metersPerPixel = 156543.03392 * cos(latitude * pi / 180) / pow(2, zoom); - double visibleWidthInMeters = metersPerPixel * screenWidthPx; - return (visibleWidthInMeters / 2) / 1000; // radius in KM - } } diff --git a/packages/inspection/lib/presentation/pages/inspection_map/map_widget.dart b/packages/inspection/lib/presentation/pages/inspection_map/map_widget.dart deleted file mode 100644 index 5b981f4..0000000 --- a/packages/inspection/lib/presentation/pages/inspection_map/map_widget.dart +++ /dev/null @@ -1,108 +0,0 @@ -// widgets/map_widgets.dart -import 'package:flutter/material.dart'; -import 'package:rasadyar_core/core.dart'; -import 'package:rasadyar_inspection/presentation/pages/inspection_map/logic.dart'; -import 'package:rasadyar_inspection/presentation/widget/search.dart'; - -class MapView extends GetView { - const MapView({super.key}); - - @override - Widget build(BuildContext context) { - return Expanded( - child: Stack( - fit: StackFit.expand, - children: [ - _buildFlutterMap(), - _buildSearchOverlay(), - ], - ), - ); - } - - Widget _buildFlutterMap() { - return ObxValue( - (currentLocation) => FlutterMap( - mapController: controller.animatedMapController.mapController, - options: MapOptions( - initialCenter: currentLocation.value, - interactionOptions: const InteractionOptions( - flags: InteractiveFlag.all & ~InteractiveFlag.rotate, - ), - initialZoom: 15, - onPositionChanged: _handlePositionChanged, - ), - children: [ - _buildTileLayer(), - _buildMarkerClusterLayer(), - ], - ), - controller.currentLocation, - ); - } - - Widget _buildTileLayer() { - return TileLayer( - urlTemplate: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png', - userAgentPackageName: 'ir.mnpc.rasadyar', - ); - } - - Widget _buildMarkerClusterLayer() { - return ObxValue( - (markers) => MarkerClusterLayerWidget( - options: MarkerClusterLayerOptions( - maxClusterRadius: 80, - size: const Size(40, 40), - alignment: Alignment.center, - padding: const EdgeInsets.all(50), - maxZoom: 15, - markers: markers.value, - builder: _buildClusterMarker, - ), - ), - controller.markers, - ); - } - - Widget _buildClusterMarker(BuildContext context, List clusterMarkers) { - return Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(20), - color: Colors.blue, - ), - child: Center( - child: Text( - clusterMarkers.length.toString(), - style: const TextStyle(color: Colors.white), - ), - ), - ); - } - - Widget _buildSearchOverlay() { - return Positioned( - top: 10, - left: 20, - right: 20, - child: ObxValue( - (isSearchSelected) => isSearchSelected.value - ? SearchWidget( - onSearchChanged: (data) { - controller.baseLogic.searchValue.value = data; - }, - ) - : const SizedBox.shrink(), - controller.baseLogic.isSearchSelected, - ), - ); - } - - void _handlePositionChanged(MapCamera camera, bool hasGesture) { - wLog(camera.zoom); - controller.debouncedUpdateVisibleMarkers( - center: camera.center, - zoom: camera.zoom, - ); - } -} diff --git a/packages/inspection/lib/presentation/pages/inspection_map/view.dart b/packages/inspection/lib/presentation/pages/inspection_map/view.dart index 044567d..b83c6f8 100644 --- a/packages/inspection/lib/presentation/pages/inspection_map/view.dart +++ b/packages/inspection/lib/presentation/pages/inspection_map/view.dart @@ -1,11 +1,11 @@ import 'package:flutter/material.dart'; import 'package:rasadyar_core/core.dart'; -import 'package:rasadyar_inspection/data/model/response/poultry_location/poultry_location_model.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/custom_chips.dart'; import 'logic.dart'; +import 'widget/map/view.dart'; class InspectionMapPage extends GetView { const InspectionMapPage({super.key}); @@ -18,109 +18,24 @@ class InspectionMapPage extends GetView { hasBack: false, defaultSearch: false, filteringWidget: filterWidget(showIndex: 3.obs, filterIndex: 5.obs), - onSearchTap: _handleSearchTap, - widgets: [_buildMap()], - floatingActionButton: _buildGpsButton(), - ); - } - - void _handleSearchTap() { - controller.baseLogic.isSearchSelected.value = !controller.baseLogic.isSearchSelected.value; - } - - Widget _buildMap() { - return Expanded( - child: Stack( - fit: StackFit.expand, - children: [ - ObxValue((currentLocation) { - return FlutterMap( - mapController: controller.animatedMapController.mapController, - options: MapOptions( - initialCenter: currentLocation.value, - interactionOptions: const InteractionOptions( - flags: InteractiveFlag.all & ~InteractiveFlag.rotate, - ), - initialZoom: 15, - onPositionChanged: (camera, hasGesture) { - controller.debouncedUpdateVisibleMarkers( - center: camera.center, - zoom: camera.zoom, - ); - }, - ), - - children: [ - TileLayer( - urlTemplate: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png', - userAgentPackageName: 'ir.mnpc.rasadyar', - ), - - ObxValue((markers) { - return MarkerClusterLayerWidget( - options: MarkerClusterLayerOptions( - maxClusterRadius: 80, - size: const Size(40, 40), - alignment: Alignment.center, - padding: const EdgeInsets.all(50), - maxZoom: 15, - markers: buildMarkers(markers), - builder: (context, clusterMarkers) { - return Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(20), - color: Colors.blue, - ), - child: Center( - child: Text( - clusterMarkers.length.toString(), - style: const TextStyle(color: Colors.white), - ), - ), - ); - }, - ), - ); - }, controller.markers2), - ], - ); - }, controller.currentLocation), - - 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, - child: ObxValue((data) { - if (data.value) { - return SearchWidget( - onSearchChanged: (data) { - controller.baseLogic.searchValue.value = data; - }, - ); - } else { - return SizedBox.shrink(); - } - }, controller.baseLogic.isSearchSelected), - ),*/ - ], - ), + widgets: [ + MapPage(), + ObxValue((p0) => Text(p0.toString()), controller.showIndex), + ObxValue((data) { + if (data.value) { + WidgetsBinding.instance.addPostFrameCallback((_) { + Get.bottomSheet( + searchWidget(), + isScrollControlled: true, + isDismissible: true, + ignoreSafeArea: false, + ); + controller.baseLogic.isSearchSelected.value = false; + }); + } + return const SizedBox.shrink(); + }, controller.baseLogic.isSearchSelected), + ], ); } @@ -130,44 +45,63 @@ class InspectionMapPage extends GetView { 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), - ), - ), + Row( + spacing: 12, + children: [ + Expanded( + 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.baseLogic.searchTextController.clear(); + controller.baseLogic.searchValue.value = null; + controller.baseLogic.isSearchSelected.value = false; + controller. mapLogic.hasFilterOrSearch.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, + ), ), - 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, + GestureDetector( + onTap: () { + + Get.back(); + }, + child: Assets.vec.mapSvg.svg( + width: 24.w, + height: 24.h, + colorFilter: ColorFilter.mode(AppColor.blueNormal, BlendMode.srcIn), + ), + ), + ], ), Expanded( child: ObxValue((rxData) { @@ -244,16 +178,7 @@ class InspectionMapPage extends GetView { ); } - 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); - } + /* Widget selectedLocationWidget2({ required bool showHint, @@ -345,14 +270,16 @@ class InspectionMapPage extends GetView { height: 40.h, backgroundColor: AppColor.blueNormal, onPressed: () { - /*controller.setEditData(item); + */ + /*controller.setEditData(item); Get.bottomSheet( addOrEditBottomSheet(true), isScrollControlled: true, backgroundColor: Colors.transparent, ).whenComplete(() { - });*/ + });*/ /* + }, child: Row( mainAxisAlignment: MainAxisAlignment.start, @@ -438,368 +365,7 @@ class InspectionMapPage extends GetView { ); }, controller.isSelectedDetailsLocation); } - - List buildMarkers(RxList markers) { - final visibleBounds = controller.animatedMapController.mapController.camera.visibleBounds; - final isZoomedIn = controller.currentZoom > 17; - - final updatedMarkers = markers.map((location) { - final point = LatLng(location.lat ?? 0, location.long ?? 0); - final isVisible = visibleBounds.contains(point); - - return Marker( - point: point, - width: isZoomedIn && isVisible ? 180.w : 40.h, - height: isZoomedIn && isVisible ? 50.h : 50.h, - 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( - 'جوجه ریزی فعال', - style: AppFonts.yekan14.copyWith( - color: AppColor.textColor, - ), - ), - - Text( - hasHatching ? 'دارد' : 'ندارد', - style: AppFonts.yekan14.copyWith( - color: AppColor.blueNormal, - ), - ), - ], - ), - ), - 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, - ), - ), - 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, - ), - ), - ], - ), - ), - ], - ), - ), - ], - ), - ], - ), - 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), - ), - - Text( - location.hatching?.first.date?.formattedJalaliDate ?? 'N/A', - style: AppFonts.yekan14.copyWith(color: AppColor.blueNormal), - ), - ], - ), - ), - buildRow( - title: 'باقیمانده', - value: location.hatching?.first.leftOver.separatedByComma ?? 'N/A', - ), - buildRow( - title: 'سن جوجه ریزی', - value: '${location.hatching?.first.chickenAge ?? 'N/A'} روز', - ), - buildRow( - title: 'شماره مجوز جوجه ریزی', - value: location.hatching?.first.licenceNumber.toString() ?? 'N/A', - ), - ], - ), - ), - ), - ], - ), - ); - }, controller.isSelectedDetailsLocation), - isScrollControlled: true, - isDismissible: true, - ); - }, - child: isZoomedIn && isVisible - ? Container( - height: 30.h, - padding: EdgeInsets.all(5.r), - decoration: BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.circular(15.r), - boxShadow: [ - BoxShadow( - color: Colors.black.withValues(alpha: 0.1), - blurRadius: 5, - offset: const Offset(0, 2), - ), - ], - ), - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - spacing: 8, - children: [ - Assets.vec.chickenMapMarkerSvg.svg(width: 24.w, height: 24.h), - Text(location.user?.fullname ?? '', style: AppFonts.yekan12), - ], - ), - ) - : Assets.vec.chickenMapMarkerSvg.svg(width: 24.w, height: 24.h), - ), - ); - }).toList(); - - return updatedMarkers; - } -} - -Marker markerWidget({required LatLng marker, required VoidCallback onTap}) { - iLog('lat: ${marker.latitude}, lng: ${marker.longitude}'); - 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}) { diff --git a/packages/inspection/lib/presentation/pages/inspection_map/widget/map/logic.dart b/packages/inspection/lib/presentation/pages/inspection_map/widget/map/logic.dart new file mode 100644 index 0000000..ff05984 --- /dev/null +++ b/packages/inspection/lib/presentation/pages/inspection_map/widget/map/logic.dart @@ -0,0 +1,139 @@ +import 'dart:async'; +import 'dart:math'; + +import 'package:flutter/material.dart'; +import 'package:rasadyar_core/core.dart'; +import 'package:rasadyar_inspection/data/model/response/poultry_location/poultry_location_model.dart'; +import 'package:rasadyar_inspection/presentation/widget/base_page/logic.dart'; + +class MapLogic extends GetxController with GetTickerProviderStateMixin { + RxBool isLoading = false.obs; + RxBool isSelectedDetailsLocation = false.obs; + RxDouble currentZoom = (15.0).obs; + Rx mapController = MapController().obs; + BaseLogic baseLogic = Get.find(); + + Rx currentLocation = LatLng(34.798315281272544, 48.51479142983491).obs; + RxBool hasFilterOrSearch = false.obs; + + Timer? _debounceTimer; + + final distance = Distance(); + + late final AnimatedMapController animatedMapController; + + Rx>> markerLocations = + Resource>.initial().obs; + + Rx>> allLocations = + Resource>.initial().obs; + + Rx>> filteredLocations = + Resource>.initial().obs; + + @override + void onInit() { + super.onInit(); + animatedMapController = AnimatedMapController( + vsync: this, + duration: const Duration(milliseconds: 500), + curve: Curves.easeInOut, + cancelPreviousAnimations: true, + ); + + ever(hasFilterOrSearch, (callback) { + if (callback) { + markerLocations.value = filteredLocations.value; + } else { + markerLocations.value = allLocations.value; + } + }); + + ever(allLocations, (_) { + if (!hasFilterOrSearch.value) { + markerLocations.value = allLocations.value; + } + }); + + ever(filteredLocations, (_) { + if (hasFilterOrSearch.value) { + markerLocations.value = filteredLocations.value; + } + }); + } + + @override + void onReady() { + // TODO: implement onReady + super.onReady(); + } + + @override + void onClose() { + // TODO: implement onClose + super.onClose(); + } + + Future 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(PoultryLocationModel( + lat: latLng.latitude, + long: latLng.longitude + ));*/ + animatedMapController.animateTo( + dest: latLng, + zoom: 18, + curve: Curves.easeInOut, + duration: const Duration(seconds: 1), + ); + isLoading.value = false; + } + + /* void debouncedUpdateVisibleMarkers({required LatLng center, required double zoom}) { + _debounceTimer?.cancel(); + _debounceTimer = Timer(const Duration(milliseconds: 300), () { + final radius = getVisibleRadiusKm( + zoom: zoom, + screenWidthPx: Get.width.toDouble(), + latitude: center.latitude, + ); + + final filtered = filterNearbyMarkers( + allPoultryLocation.value.data ?? [], + center.latitude, + center.longitude, + radius, + ); + final existingIds = markers2.map((e) => e.id).toSet(); + final uniqueFiltered = filtered.where((e) => !existingIds.contains(e.id)).toList(); + markers2.addAll(uniqueFiltered); + }); + }*/ + + List filterNearbyMarkers( + List allMarkers, + double centerLat, + double centerLng, + double radiusInMeters, + ) { + final center = LatLng(centerLat, centerLng); + + return allMarkers.where((marker) => distance(center, marker) <= radiusInMeters).toList(); + } + + double getVisibleRadiusKm({ + required double zoom, + required double screenWidthPx, + required double latitude, + }) { + double metersPerPixel = 156543.03392 * cos(latitude * pi / 180) / pow(2, zoom); + double visibleWidthInMeters = metersPerPixel * screenWidthPx; + return (visibleWidthInMeters / 2); // radius in Meter + } +} diff --git a/packages/inspection/lib/presentation/pages/inspection_map/widget/map/view.dart b/packages/inspection/lib/presentation/pages/inspection_map/widget/map/view.dart new file mode 100644 index 0000000..c532774 --- /dev/null +++ b/packages/inspection/lib/presentation/pages/inspection_map/widget/map/view.dart @@ -0,0 +1,496 @@ +import 'package:flutter/material.dart'; +import 'package:rasadyar_core/core.dart'; +import 'package:rasadyar_inspection/data/model/response/poultry_location/poultry_location_model.dart'; + +import 'logic.dart'; + +class MapPage extends GetView { + const MapPage({super.key}); + + @override + Widget build(BuildContext context) { + return Expanded( + child: Stack( + fit: StackFit.expand, + children: [ + ObxValue((currentLocation) { + return FlutterMap( + mapController: controller.animatedMapController.mapController, + options: MapOptions( + initialCenter: currentLocation.value, + interactionOptions: const InteractionOptions( + flags: InteractiveFlag.all & ~InteractiveFlag.rotate, + ), + initialZoom: 15, + onPositionChanged: (camera, hasGesture) { + //controller.debouncedUpdateVisibleMarkers(center: camera.center, zoom: camera.zoom); + }, + ), + + children: [ + TileLayer( + urlTemplate: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png', + userAgentPackageName: 'ir.mnpc.rasadyar', + ), + + ObxValue((markers) { + if (markers.value.status == ResourceStatus.success) { + return MarkerLayer( + markers: List.generate(markers.value.data?.length ?? 0, (index) { + final location = markers.value.data![index]; + return markerWidget( + marker: location.latLng ?? LatLng(0, 0), + onTap: () { + controller.isSelectedDetailsLocation.value = true; + controller.animatedMapController.animateTo( + dest: location.latLng ?? LatLng(0, 0), + zoom: 18, + ); + }, + ); + }), + ); + } + return Container(width: 20, height: 20, color: Colors.lightGreen); + }, controller.markerLocations), + + /* ObxValue((markers) { + return MarkerClusterLayerWidget( + options: MarkerClusterLayerOptions( + maxClusterRadius: 80, + size: const Size(40, 40), + alignment: Alignment.center, + padding: const EdgeInsets.all(50), + maxZoom: 15, + markers: buildMarkers(markers), + builder: (context, clusterMarkers) { + return Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(20), + color: Colors.blue, + ), + child: Center( + child: Text( + clusterMarkers.length.toString(), + style: const TextStyle(color: Colors.white), + ), + ), + ); + }, + ), + ); + }, controller.allLocations),*/ + ], + ); + }, controller.currentLocation), + + /* 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, + 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); + } + + List buildMarkers(RxList markers) { + final visibleBounds = controller.animatedMapController.mapController.camera.visibleBounds; + final isZoomedIn = controller.currentZoom > 17; + + final updatedMarkers = markers.map((location) { + final point = LatLng(location.lat ?? 0, location.long ?? 0); + final isVisible = visibleBounds.contains(point); + + return Marker( + point: point, + width: isZoomedIn && isVisible ? 180.w : 40.h, + height: isZoomedIn && isVisible ? 50.h : 50.h, + 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( + 'جوجه ریزی فعال', + style: AppFonts.yekan14.copyWith( + color: AppColor.textColor, + ), + ), + + Text( + hasHatching ? 'دارد' : 'ندارد', + style: AppFonts.yekan14.copyWith( + color: AppColor.blueNormal, + ), + ), + ], + ), + ), + 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, + ), + ), + 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, + ), + ), + ], + ), + ), + ], + ), + ), + ], + ), + ], + ), + 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), + ), + + Text( + location.hatching?.first.date?.formattedJalaliDate ?? 'N/A', + style: AppFonts.yekan14.copyWith(color: AppColor.blueNormal), + ), + ], + ), + ), + buildRow( + title: 'باقیمانده', + value: location.hatching?.first.leftOver.separatedByComma ?? 'N/A', + ), + buildRow( + title: 'سن جوجه ریزی', + value: '${location.hatching?.first.chickenAge ?? 'N/A'} روز', + ), + buildRow( + title: 'شماره مجوز جوجه ریزی', + value: location.hatching?.first.licenceNumber.toString() ?? 'N/A', + ), + ], + ), + ), + ), + ], + ), + ); + }, controller.isSelectedDetailsLocation), + isScrollControlled: true, + isDismissible: true, + ); + }, + child: isZoomedIn && isVisible + ? Container( + height: 30.h, + padding: EdgeInsets.all(5.r), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(15.r), + boxShadow: [ + BoxShadow( + color: Colors.black.withValues(alpha: 0.1), + blurRadius: 5, + offset: const Offset(0, 2), + ), + ], + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + spacing: 8, + children: [ + Assets.vec.chickenMapMarkerSvg.svg(width: 24.w, height: 24.h), + Text(location.user?.fullname ?? '', style: AppFonts.yekan12), + ], + ), + ) + : Assets.vec.chickenMapMarkerSvg.svg(width: 24.w, height: 24.h), + ), + ); + }).toList(); + + return updatedMarkers; + } + + 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), + ), + ), + ); + } +} diff --git a/packages/inspection/lib/presentation/routes/app_pages.dart b/packages/inspection/lib/presentation/routes/app_pages.dart index a6cc79a..02ac975 100644 --- a/packages/inspection/lib/presentation/routes/app_pages.dart +++ b/packages/inspection/lib/presentation/routes/app_pages.dart @@ -2,6 +2,7 @@ import 'package:rasadyar_core/core.dart'; import 'package:rasadyar_inspection/presentation/pages/auth/logic.dart'; import 'package:rasadyar_inspection/presentation/pages/auth/view.dart'; import 'package:rasadyar_inspection/presentation/pages/filter/logic.dart'; +import 'package:rasadyar_inspection/presentation/pages/inspection_map/widget/map/logic.dart'; import 'package:rasadyar_inspection/presentation/pages/pages.dart'; import 'package:rasadyar_inspection/presentation/pages/users/logic.dart'; import 'package:rasadyar_inspection/presentation/routes/app_routes.dart'; @@ -15,7 +16,7 @@ sealed class InspectionPages { GetPage( name: InspectionRoutes.init, page: () => RootPage(), - middlewares:[ AuthMiddleware()], + middlewares: [AuthMiddleware()], binding: BindingsBuilder(() { Get.lazyPut(() => RootLogic()); Get.lazyPut(() => InspectorFilterLogic()); @@ -25,8 +26,9 @@ sealed class InspectionPages { Get.lazyPut(() => RecordsLogic()); Get.lazyPut(() => StatisticsLogic()); Get.lazyPut(() => LocationDetailsLogic(), fenix: true); - Get.lazyPut(() => ActionLogic(), fenix: true); - Get.lazyPut(() => ProfileLogic(), fenix: true); + Get.lazyPut(() => ActionLogic()); + Get.lazyPut(() => ProfileLogic()); + Get.lazyPut(() => MapLogic()); }), ), @@ -67,7 +69,7 @@ sealed class InspectionPages { page: () => AuthPage(), binding: BindingsBuilder(() { Get.lazyPut(() => AuthLogic()); - Get.lazyPut(() =>CaptchaWidgetLogic()); + Get.lazyPut(() => CaptchaWidgetLogic()); }), ), ];