You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

148 lines
4.4 KiB

1 month ago
import 'dart:math';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:geolocator/geolocator.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:permission_handler/permission_handler.dart';
class ServiceLocationsRadiusScreen extends StatefulWidget {
final LatLng? initialPosition;
final ValueListenable<double>? radiusKmListenable;
const ServiceLocationsRadiusScreen({
Key? key,
this.initialPosition,
this.radiusKmListenable,
}) : super(key: key);
@override
State<ServiceLocationsRadiusScreen> createState() =>
_ServiceLocationsRadiusScreenState();
}
class _ServiceLocationsRadiusScreenState
extends State<ServiceLocationsRadiusScreen> {
GoogleMapController? _controller;
LatLng? _currentPosition;
LatLng? _circleCenter;
bool _isMapReady = false;
Set<Circle> _circles = {};
final double _fallbackRadiusMeters = 10000; // 10 km
final LatLng _indiaCenter = const LatLng(20.5937, 78.9629);
@override
void initState() {
super.initState();
if (widget.initialPosition == null) {
_checkLocationPermissionAndGetCurrentPosition();
} else {
_circleCenter = widget.initialPosition;
}
widget.radiusKmListenable?.addListener(_onRadiusChangedFromParent);
}
@override
void dispose() {
widget.radiusKmListenable?.removeListener(_onRadiusChangedFromParent);
super.dispose();
}
// 🔹 When radius changes in parent
void _onRadiusChangedFromParent() {
final km =
widget.radiusKmListenable?.value ?? (_fallbackRadiusMeters / 1000.0);
final meters = km * 1000.0;
final center = _circleCenter ?? _currentPosition ?? _indiaCenter;
_addRadiusCircle(center, radiusInMeters: meters);
}
// 🔹 Get current location
Future<void> _checkLocationPermissionAndGetCurrentPosition() async {
var status = await Permission.location.status;
if (!status.isGranted) {
status = await Permission.location.request();
if (!status.isGranted) return;
}
bool serviceEnabled = await Geolocator.isLocationServiceEnabled();
if (!serviceEnabled) return;
final position = await Geolocator.getCurrentPosition();
setState(() {
_currentPosition = LatLng(position.latitude, position.longitude);
});
}
// 🔹 Add / update the circle
void _addRadiusCircle(LatLng center, {double? radiusInMeters}) async {
final meters = radiusInMeters ??
(widget.radiusKmListenable?.value ?? (_fallbackRadiusMeters / 1000.0)) *
1000.0;
setState(() {
_circleCenter = center;
_circles = {
Circle(
circleId: const CircleId("radius_circle"),
center: center,
radius: meters,
fillColor: Colors.blue.withOpacity(0.2),
strokeColor: Colors.blueAccent,
strokeWidth: 2,
),
};
});
// 👇 adjust zoom so full circle is visible even in small container
if (_controller != null) {
final zoom = _getZoomLevelForSmallMap(meters);
await _controller!.animateCamera(
CameraUpdate.newCameraPosition(
CameraPosition(target: center, zoom: zoom),
),
);
}
}
// 🔹 Calculate zoom for small map container (200300 px)
double _getZoomLevelForSmallMap(double radiusMeters) {
// Adjust constant here if map size differs
final scale = radiusMeters / 200; // Smaller scale for small map
double zoomLevel = 16 - log(scale) / log(2);
if (zoomLevel > 18) zoomLevel = 18;
if (zoomLevel < 4) zoomLevel = 4;
return zoomLevel;
}
@override
Widget build(BuildContext context) {
final LatLng target =
widget.initialPosition ?? _currentPosition ?? _indiaCenter;
final initialCameraPosition = CameraPosition(
target: target,
zoom: 12,
);
return GoogleMap(
initialCameraPosition: initialCameraPosition,
mapType: MapType.normal,
circles: _circles,
zoomControlsEnabled: false,
myLocationEnabled: false,
myLocationButtonEnabled: false,
onMapCreated: (controller) async {
_controller = controller;
_isMapReady = true;
await Future.delayed(const Duration(milliseconds: 500));
final center = widget.initialPosition ?? _currentPosition ?? _indiaCenter;
final km = widget.radiusKmListenable?.value ?? 10;
_addRadiusCircle(center, radiusInMeters: km * 1000.0);
},
);
}
}