feat: implement MapCameraLocation widget for capturing camera previews with integrated map and location overlays
This commit is contained in:
@@ -1,53 +1,20 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
import 'dart:io';
|
||||||
|
import 'dart:math';
|
||||||
|
|
||||||
|
import 'package:camera/camera.dart';
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/rendering.dart';
|
||||||
|
import 'package:flutter_map/flutter_map.dart';
|
||||||
|
import 'package:geocoding/geocoding.dart';
|
||||||
import 'package:geolocator/geolocator.dart';
|
import 'package:geolocator/geolocator.dart';
|
||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
import 'dart:ui' as ui;
|
import 'dart:ui' as ui;
|
||||||
import 'package:latlong2/latlong.dart' as lat;
|
import 'package:latlong2/latlong.dart' as lat;
|
||||||
import 'package:map_camera_flutter/map_camera_flutter.dart';
|
import 'image_and_location_data.dart'; // Standard local import
|
||||||
|
import 'components/location_details_widget.dart'; // Standard local import
|
||||||
///import 'package:your_app/map_camera_flutter.dart'; // Import the file where the MapCameraLocation widget is defined
|
import 'package:path_provider/path_provider.dart';
|
||||||
|
|
||||||
/// ```
|
|
||||||
/// void main() {
|
|
||||||
/// final cameras = await availableCameras();
|
|
||||||
/// final firstCamera = cameras.first;
|
|
||||||
/// runApp(MyApp(camera: firstCamera));
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// class MyApp extends StatelessWidget {
|
|
||||||
/// final CameraDescription camera;
|
|
||||||
/// const MyApp({super.key, required this.camera});
|
|
||||||
///
|
|
||||||
/// @override
|
|
||||||
/// Widget build(BuildContext context) {
|
|
||||||
/// return MaterialApp(
|
|
||||||
/// home: CameraLocationScreen(camera: firstCamera),
|
|
||||||
/// );
|
|
||||||
/// }
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// class CameraLocationScreen extends StatelessWidget {
|
|
||||||
/// final CameraDescription camera;
|
|
||||||
/// const MyApp({super.key, required this.camera});
|
|
||||||
// // Callback function to handle the captured image and location data
|
|
||||||
/// void handleImageAndLocationData(ImageAndLocationData data) {
|
|
||||||
// // You can use the data here as needed
|
|
||||||
/// print('Image Path: ${data.imagePath}');
|
|
||||||
/// print('Latitude: ${data.latitude}');
|
|
||||||
/// print('Longitude: ${data.longitude}');
|
|
||||||
/// print('Location Name: ${data.locationName}');
|
|
||||||
/// print('SubLocation: ${data.subLocation}');
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// @override
|
|
||||||
/// Widget build(BuildContext context) {
|
|
||||||
/// // Provide the CameraDescription and the handleImageAndLocationData callback function to the MapCameraLocation widget
|
|
||||||
/// return MapCameraLocation(
|
|
||||||
/// camera: camera, // YOUR_CAMERA_DESCRIPTION_OBJECT, // Replace YOUR_CAMERA_DESCRIPTION_OBJECT with your actual CameraDescription
|
|
||||||
/// onImageCaptured: handleImageAndLocationData,
|
|
||||||
/// );
|
|
||||||
/// }
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
|
|
||||||
// Callback function type for capturing image and location data
|
// Callback function type for capturing image and location data
|
||||||
typedef ImageAndLocationCallback = void Function(ImageAndLocationData data);
|
typedef ImageAndLocationCallback = void Function(ImageAndLocationData data);
|
||||||
@@ -57,18 +24,17 @@ class MapCameraLocation extends StatefulWidget {
|
|||||||
final ImageAndLocationCallback? onImageCaptured;
|
final ImageAndLocationCallback? onImageCaptured;
|
||||||
final String userAgent;
|
final String userAgent;
|
||||||
final String packageName;
|
final String packageName;
|
||||||
|
final double? latitude;
|
||||||
|
final double? longitude;
|
||||||
|
|
||||||
/// Constructs a MapCameraLocation widget.
|
|
||||||
///
|
|
||||||
/// The [camera] parameter is required and represents the camera to be used for capturing images.
|
|
||||||
/// The [onImageCaptured] parameter is an optional callback function that will be triggered when an image and location data are captured.
|
|
||||||
/// The [userAgent], and [packageName] parameters are required for open street map policies https://operations.osmfoundation.org/policies/tiles.
|
|
||||||
const MapCameraLocation({
|
const MapCameraLocation({
|
||||||
super.key,
|
super.key,
|
||||||
required this.camera,
|
required this.camera,
|
||||||
this.onImageCaptured,
|
this.onImageCaptured,
|
||||||
required this.userAgent,
|
required this.userAgent,
|
||||||
required this.packageName,
|
required this.packageName,
|
||||||
|
this.latitude,
|
||||||
|
this.longitude,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -77,46 +43,18 @@ class MapCameraLocation extends StatefulWidget {
|
|||||||
|
|
||||||
class _MapCameraLocationState extends State<MapCameraLocation> {
|
class _MapCameraLocationState extends State<MapCameraLocation> {
|
||||||
late CameraController _controller;
|
late CameraController _controller;
|
||||||
|
|
||||||
/// Represents a controller for the camera, used to control camera-related operations.
|
|
||||||
|
|
||||||
late Future<void> _initializeControllerFuture;
|
late Future<void> _initializeControllerFuture;
|
||||||
|
|
||||||
/// Represents a future that resolves when the camera controller has finished initializing.
|
|
||||||
|
|
||||||
late AlignOnUpdate _followOnLocationUpdate;
|
late AlignOnUpdate _followOnLocationUpdate;
|
||||||
|
|
||||||
/// Enum value indicating when to follow location updates.
|
|
||||||
|
|
||||||
late StreamController<double?> _followCurrentLocationStreamController;
|
late StreamController<double?> _followCurrentLocationStreamController;
|
||||||
|
|
||||||
/// Stream controller used to track the current location.
|
|
||||||
|
|
||||||
File? cameraImagePath;
|
File? cameraImagePath;
|
||||||
|
|
||||||
/// File path of the captured camera image.
|
|
||||||
|
|
||||||
File? ssImage;
|
File? ssImage;
|
||||||
|
|
||||||
/// File path of the captured screen shot image.
|
|
||||||
|
|
||||||
String? dateTime;
|
String? dateTime;
|
||||||
|
|
||||||
/// A formatted string representing the current date and time.
|
|
||||||
|
|
||||||
final globalKey = GlobalKey();
|
final globalKey = GlobalKey();
|
||||||
|
|
||||||
/// Key used to uniquely identify and control a widget.
|
|
||||||
|
|
||||||
Placemark? placeMark;
|
Placemark? placeMark;
|
||||||
|
|
||||||
/// Represents geocoded location information.
|
|
||||||
|
|
||||||
LocationData? locationData;
|
LocationData? locationData;
|
||||||
|
|
||||||
/// SubLocation of the current location as a string.
|
|
||||||
|
|
||||||
/// Callback function to retrieve the image and location data.
|
|
||||||
ImageAndLocationData getImageAndLocationData() {
|
ImageAndLocationData getImageAndLocationData() {
|
||||||
return ImageAndLocationData(
|
return ImageAndLocationData(
|
||||||
imagePath: cameraImagePath?.path,
|
imagePath: cameraImagePath?.path,
|
||||||
@@ -125,16 +63,22 @@ class _MapCameraLocationState extends State<MapCameraLocation> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Timer? _positionTimer;
|
Timer? _positionTimer;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
_positionTimer = Timer.periodic(const Duration(seconds: 1), (timer) async {
|
|
||||||
if (mounted) {
|
|
||||||
await updatePosition(context);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Initialize the camera controller
|
// Support manual location injection for "Source of Truth" from Home screen
|
||||||
|
if (widget.latitude != null && widget.longitude != null) {
|
||||||
|
locationData = LocationData(
|
||||||
|
latitude: widget.latitude.toString(),
|
||||||
|
longitude: widget.longitude.toString(),
|
||||||
|
locationName: 'Fetching address...',
|
||||||
|
subLocation: '',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize camera
|
||||||
_controller = CameraController(
|
_controller = CameraController(
|
||||||
widget.camera,
|
widget.camera,
|
||||||
ResolutionPreset.medium,
|
ResolutionPreset.medium,
|
||||||
@@ -143,7 +87,15 @@ class _MapCameraLocationState extends State<MapCameraLocation> {
|
|||||||
_followOnLocationUpdate = AlignOnUpdate.always;
|
_followOnLocationUpdate = AlignOnUpdate.always;
|
||||||
_followCurrentLocationStreamController = StreamController<double?>();
|
_followCurrentLocationStreamController = StreamController<double?>();
|
||||||
|
|
||||||
// Get the current date and time in a formatted string
|
// Initial fetch and start periodic updates
|
||||||
|
// Reduce frequency to 5 seconds to be more battery/CPU efficient
|
||||||
|
Future.microtask(() => updatePosition(context));
|
||||||
|
_positionTimer = Timer.periodic(const Duration(seconds: 5), (timer) {
|
||||||
|
if (mounted) {
|
||||||
|
updatePosition(context);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
dateTime = DateFormat.yMd().add_jm().format(DateTime.now());
|
dateTime = DateFormat.yMd().add_jm().format(DateTime.now());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -160,7 +112,7 @@ class _MapCameraLocationState extends State<MapCameraLocation> {
|
|||||||
if (mounted) {
|
if (mounted) {
|
||||||
super.setState(fn);
|
super.setState(fn);
|
||||||
} else {
|
} else {
|
||||||
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
super.setState(fn);
|
super.setState(fn);
|
||||||
}
|
}
|
||||||
@@ -176,14 +128,15 @@ class _MapCameraLocationState extends State<MapCameraLocation> {
|
|||||||
future: _initializeControllerFuture,
|
future: _initializeControllerFuture,
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
if (snapshot.connectionState == ConnectionState.done) {
|
if (snapshot.connectionState == ConnectionState.done) {
|
||||||
|
final currentLat = double.tryParse(locationData?.latitude ?? '0') ?? 0;
|
||||||
|
final currentLng = double.tryParse(locationData?.longitude ?? '0') ?? 0;
|
||||||
|
|
||||||
return Center(
|
return Center(
|
||||||
child: RepaintBoundary(
|
child: RepaintBoundary(
|
||||||
key: globalKey,
|
key: globalKey,
|
||||||
child: Stack(
|
child: Stack(
|
||||||
children: [
|
children: [
|
||||||
CameraPreview(
|
CameraPreview(_controller),
|
||||||
_controller,
|
|
||||||
),
|
|
||||||
Positioned(
|
Positioned(
|
||||||
left: 0,
|
left: 0,
|
||||||
right: 0,
|
right: 0,
|
||||||
@@ -191,84 +144,64 @@ class _MapCameraLocationState extends State<MapCameraLocation> {
|
|||||||
child: Column(
|
child: Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
SizedBox(
|
if (locationData != null)
|
||||||
height: 160,
|
SizedBox(
|
||||||
child: Row(
|
height: 160,
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
child: Row(
|
||||||
children: [
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
Padding(
|
children: [
|
||||||
padding: const EdgeInsets.symmetric(
|
Padding(
|
||||||
horizontal: 8.0),
|
padding: const EdgeInsets.symmetric(horizontal: 8.0),
|
||||||
child: Card(
|
child: Card(
|
||||||
elevation: 3,
|
elevation: 3,
|
||||||
shape: RoundedRectangleBorder(
|
shape: RoundedRectangleBorder(
|
||||||
borderRadius:
|
borderRadius: BorderRadius.circular(8.0),
|
||||||
BorderRadius.circular(8.0)),
|
),
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
// height: 130,
|
width: 120,
|
||||||
width: 120,
|
child: Padding(
|
||||||
child: Padding(
|
padding: const EdgeInsets.all(5.0),
|
||||||
padding: const EdgeInsets.all(5.0),
|
child: FlutterMap(
|
||||||
child: locationData == null
|
options: MapOptions(
|
||||||
? const Center(
|
initialCenter: lat.LatLng(currentLat, currentLng),
|
||||||
child:
|
initialZoom: 14.0,
|
||||||
CircularProgressIndicator())
|
onPositionChanged: (pos, hasGesture) {
|
||||||
: FlutterMap(
|
if (hasGesture) {
|
||||||
options: MapOptions(
|
setState(() => _followOnLocationUpdate = AlignOnUpdate.never);
|
||||||
initialCenter:
|
}
|
||||||
const lat.LatLng(0, 0),
|
},
|
||||||
initialZoom: 13.0,
|
),
|
||||||
onPositionChanged: (position,
|
children: [
|
||||||
bool hasGesture) {
|
TileLayer(
|
||||||
if (hasGesture) {
|
urlTemplate: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
|
||||||
setState(
|
tileProvider: NetworkTileProvider(
|
||||||
() =>
|
headers: {'User-Agent': widget.userAgent},
|
||||||
_followOnLocationUpdate =
|
|
||||||
AlignOnUpdate
|
|
||||||
.never,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
children: [
|
userAgentPackageName: widget.packageName,
|
||||||
TileLayer(
|
minZoom: 12,
|
||||||
urlTemplate:
|
|
||||||
'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
|
|
||||||
tileProvider:
|
|
||||||
NetworkTileProvider(
|
|
||||||
headers: {
|
|
||||||
'User-Agent':
|
|
||||||
widget.userAgent,
|
|
||||||
},
|
|
||||||
),
|
|
||||||
userAgentPackageName:
|
|
||||||
widget.packageName,
|
|
||||||
minZoom: 12,
|
|
||||||
),
|
|
||||||
CurrentLocationLayer(
|
|
||||||
alignPositionStream:
|
|
||||||
_followCurrentLocationStreamController
|
|
||||||
.stream,
|
|
||||||
alignPositionOnUpdate:
|
|
||||||
_followOnLocationUpdate,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
|
CurrentLocationLayer(
|
||||||
|
alignPositionStream: _followCurrentLocationStreamController.stream,
|
||||||
|
alignPositionOnUpdate: _followOnLocationUpdate,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
Expanded(
|
||||||
Expanded(
|
child: LocationDetailsWidget(
|
||||||
child: LocationDetailsWidget(
|
|
||||||
locationData: locationData,
|
locationData: locationData,
|
||||||
dateTime: dateTime),
|
dateTime: dateTime,
|
||||||
),
|
),
|
||||||
const SizedBox(
|
),
|
||||||
width: 10,
|
const SizedBox(width: 10)
|
||||||
)
|
],
|
||||||
],
|
),
|
||||||
),
|
)
|
||||||
),
|
else
|
||||||
|
const Center(child: CircularProgressIndicator(color: Colors.white)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -277,7 +210,7 @@ class _MapCameraLocationState extends State<MapCameraLocation> {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return const Center(child: CircularProgressIndicator());
|
return const Center(child: CircularProgressIndicator(color: Colors.white));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@@ -285,11 +218,9 @@ class _MapCameraLocationState extends State<MapCameraLocation> {
|
|||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
try {
|
try {
|
||||||
await _initializeControllerFuture;
|
await _initializeControllerFuture;
|
||||||
takeScreenshot();
|
await takeScreenshot();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (kDebugMode) {
|
if (kDebugMode) debugPrint('Capture Error: $e');
|
||||||
print(e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
child: const Icon(Icons.camera_alt),
|
child: const Icon(Icons.camera_alt),
|
||||||
@@ -297,139 +228,112 @@ class _MapCameraLocationState extends State<MapCameraLocation> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Takes a screenshot of the current screen and saves it as an image file.
|
|
||||||
/// Returns the file path of the captured image and triggers the [onImageCaptured]
|
|
||||||
/// callback if provided.
|
|
||||||
/// Throws an exception if there is an error capturing the screenshot.
|
|
||||||
Future<void> takeScreenshot() async {
|
Future<void> takeScreenshot() async {
|
||||||
var rng = Random();
|
final rng = Random();
|
||||||
|
final boundary = globalKey.currentContext!.findRenderObject()! as RenderRepaintBoundary;
|
||||||
// Get the render boundary of the widget
|
final image = await boundary.toImage(pixelRatio: 2.0);
|
||||||
final RenderRepaintBoundary boundary =
|
|
||||||
globalKey.currentContext!.findRenderObject()! as RenderRepaintBoundary;
|
|
||||||
|
|
||||||
// Capture the screen as an image
|
|
||||||
ui.Image image = await boundary.toImage();
|
|
||||||
final directory = (await getApplicationDocumentsDirectory()).path;
|
final directory = (await getApplicationDocumentsDirectory()).path;
|
||||||
|
final byteData = await image.toByteData(format: ui.ImageByteFormat.png);
|
||||||
|
final pngBytes = byteData!.buffer.asUint8List();
|
||||||
|
final imgFile = File('$directory/screenshot${rng.nextInt(10000)}.png');
|
||||||
|
|
||||||
// Convert the image to bytes in PNG format
|
|
||||||
ByteData? byteData = await image.toByteData(format: ui.ImageByteFormat.png);
|
|
||||||
Uint8List pngBytes = byteData!.buffer.asUint8List();
|
|
||||||
|
|
||||||
// Generate a random file name for the screenshot
|
|
||||||
File imgFile = File('$directory/screenshot${rng.nextInt(200)}.png');
|
|
||||||
|
|
||||||
// Write the bytes to the file
|
|
||||||
await imgFile.writeAsBytes(pngBytes);
|
await imgFile.writeAsBytes(pngBytes);
|
||||||
|
|
||||||
// Check if the file exists
|
if (imgFile.existsSync()) {
|
||||||
bool isExists = imgFile.existsSync();
|
setState(() => cameraImagePath = imgFile);
|
||||||
|
|
||||||
if (isExists) {
|
|
||||||
// Set the file path of the captured image
|
|
||||||
setState(() {
|
|
||||||
cameraImagePath = imgFile;
|
|
||||||
});
|
|
||||||
|
|
||||||
// Trigger the image captured callback
|
|
||||||
if (widget.onImageCaptured != null) {
|
if (widget.onImageCaptured != null) {
|
||||||
ImageAndLocationData data = ImageAndLocationData(
|
widget.onImageCaptured!(ImageAndLocationData(
|
||||||
imagePath: imgFile.path,
|
imagePath: imgFile.path,
|
||||||
locationData: locationData,
|
locationData: locationData,
|
||||||
);
|
));
|
||||||
widget.onImageCaptured!(data);
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
debugPrint('File does not exist');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Updates the current position by retrieving the latitude, longitude, location name,
|
|
||||||
/// and subLocation based on the user's device location. Updates the corresponding
|
|
||||||
/// state variables with the retrieved data.
|
|
||||||
/// Throws an exception if there is an error retrieving the location information.
|
|
||||||
Future<void> updatePosition(BuildContext context) async {
|
Future<void> updatePosition(BuildContext context) async {
|
||||||
try {
|
try {
|
||||||
// Determine the current position
|
double latVal;
|
||||||
final position = await _determinePosition();
|
double lngVal;
|
||||||
|
|
||||||
// Retrieve the placeMarks for the current position
|
if (widget.latitude != null && widget.longitude != null) {
|
||||||
final placeMarks =
|
latVal = widget.latitude!;
|
||||||
await placemarkFromCoordinates(position.latitude, position.longitude);
|
lngVal = widget.longitude!;
|
||||||
|
|
||||||
LocationData locationData;
|
|
||||||
if (placeMarks.isNotEmpty) {
|
|
||||||
final placeMark = placeMarks.first;
|
|
||||||
|
|
||||||
locationData = LocationData(
|
|
||||||
latitude: position.latitude.toString(),
|
|
||||||
longitude: position.longitude.toString(),
|
|
||||||
locationName:
|
|
||||||
"${placeMark.locality ?? ""}, ${placeMark.administrativeArea ?? ""}, ${placeMark.country ?? ""}",
|
|
||||||
subLocation:
|
|
||||||
"${placeMark.street ?? ""}, ${placeMark.thoroughfare ?? ""} ${placeMark.administrativeArea ?? ""}");
|
|
||||||
} else {
|
} else {
|
||||||
locationData = LocationData(
|
final pos = await _determinePosition();
|
||||||
longitude: null,
|
latVal = pos.latitude;
|
||||||
latitude: null,
|
lngVal = pos.longitude;
|
||||||
locationName: 'No Location Data',
|
|
||||||
subLocation: "");
|
|
||||||
}
|
}
|
||||||
if (locationData != this.locationData) {
|
|
||||||
// Update the state variables with the retrieved location data
|
final placeMarks = await placemarkFromCoordinates(latVal, lngVal);
|
||||||
|
|
||||||
|
if (mounted) {
|
||||||
setState(() {
|
setState(() {
|
||||||
this.locationData = locationData;
|
if (placeMarks.isNotEmpty) {
|
||||||
|
final p = placeMarks.first;
|
||||||
|
locationData = LocationData(
|
||||||
|
latitude: latVal.toString(),
|
||||||
|
longitude: lngVal.toString(),
|
||||||
|
locationName: "${p.locality ?? ""}, ${p.administrativeArea ?? ""}, ${p.country ?? ""}",
|
||||||
|
subLocation: "${p.street ?? ""}, ${p.thoroughfare ?? ""} ${p.administrativeArea ?? ""}",
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
locationData = LocationData(
|
||||||
|
latitude: latVal.toString(),
|
||||||
|
longitude: lngVal.toString(),
|
||||||
|
locationName: 'Coordinate Found',
|
||||||
|
subLocation: "No address details",
|
||||||
|
);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (kDebugMode) {
|
|
||||||
print(
|
|
||||||
"Latitude: ${locationData.latitude}, Longitude: ${locationData.longitude}, Location: ${locationData.locationName}");
|
|
||||||
}
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// Handle any errors that occurred during location retrieval
|
if (kDebugMode) debugPrint("Location Error: $e");
|
||||||
setState(() {
|
if (locationData == null) {
|
||||||
locationData = LocationData(
|
setState(() {
|
||||||
longitude: null,
|
locationData = LocationData(
|
||||||
latitude: null,
|
latitude: null,
|
||||||
locationName: 'Error Retrieving Location',
|
longitude: null,
|
||||||
subLocation: "");
|
locationName: 'Error Loading Location',
|
||||||
});
|
subLocation: e.toString(),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Determines the current position using the GeoLocator package.
|
|
||||||
/// Returns the current position as a [Position] object.
|
|
||||||
/// Throws an exception if there is an error determining the position or if the necessary permissions are not granted.
|
|
||||||
Future<Position> _determinePosition() async {
|
Future<Position> _determinePosition() async {
|
||||||
bool serviceEnabled;
|
final serviceEnabled = await Geolocator.isLocationServiceEnabled();
|
||||||
LocationPermission permission;
|
|
||||||
|
|
||||||
// Check if location services are enabled
|
|
||||||
serviceEnabled = await Geolocator.isLocationServiceEnabled();
|
|
||||||
if (!serviceEnabled) {
|
if (!serviceEnabled) {
|
||||||
// If location services are disabled, throw an exception
|
final lastKnown = await Geolocator.getLastKnownPosition();
|
||||||
throw Exception('Location services are disabled.');
|
if (lastKnown != null) return lastKnown;
|
||||||
|
throw Exception('Location service disabled');
|
||||||
}
|
}
|
||||||
// Check location permission
|
|
||||||
permission = await Geolocator.checkPermission();
|
var permission = await Geolocator.checkPermission();
|
||||||
if (permission == LocationPermission.denied) {
|
if (permission == LocationPermission.denied) {
|
||||||
// If location permission is denied, request it
|
|
||||||
permission = await Geolocator.requestPermission();
|
permission = await Geolocator.requestPermission();
|
||||||
if (permission == LocationPermission.denied) {
|
if (permission == LocationPermission.denied) {
|
||||||
// If location permission is still denied, throw an exception
|
final lastKnown = await Geolocator.getLastKnownPosition();
|
||||||
throw Exception('Location permissions are denied');
|
if (lastKnown != null) return lastKnown;
|
||||||
|
throw Exception('Location permission denied');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if location permission is permanently denied
|
|
||||||
if (permission == LocationPermission.deniedForever) {
|
if (permission == LocationPermission.deniedForever) {
|
||||||
// Throw an exception if location permission is permanently denied
|
final lastKnown = await Geolocator.getLastKnownPosition();
|
||||||
throw Exception(
|
if (lastKnown != null) return lastKnown;
|
||||||
'Location permissions are permanently denied, we cannot request permissions.');
|
throw Exception('Location permission permanently denied');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the current position
|
try {
|
||||||
return await Geolocator.getCurrentPosition();
|
return await Geolocator.getCurrentPosition(
|
||||||
|
desiredAccuracy: LocationAccuracy.medium,
|
||||||
|
timeLimit: const Duration(seconds: 8),
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
final lastKnown = await Geolocator.getLastKnownPosition();
|
||||||
|
if (lastKnown != null) return lastKnown;
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user