feat: implement map camera widget with integrated location data capture and display
This commit is contained in:
@@ -1,5 +1,8 @@
|
||||
import 'dart:typed_data';
|
||||
|
||||
class ImageAndLocationData {
|
||||
final String? imagePath;
|
||||
final Uint8List? imageBytes;
|
||||
final LocationData? locationData;
|
||||
String? get latitude => locationData?.latitude;
|
||||
String? get longitude => locationData?.longitude;
|
||||
@@ -8,6 +11,7 @@ class ImageAndLocationData {
|
||||
|
||||
ImageAndLocationData({
|
||||
required this.imagePath,
|
||||
this.imageBytes,
|
||||
required this.locationData,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
import 'dart:io' as io;
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:camera/camera.dart';
|
||||
@@ -48,8 +48,8 @@ class _MapCameraLocationState extends State<MapCameraLocation> {
|
||||
late AlignOnUpdate _followOnLocationUpdate;
|
||||
late StreamController<double?> _followCurrentLocationStreamController;
|
||||
|
||||
File? cameraImagePath;
|
||||
File? ssImage;
|
||||
io.File? cameraImagePath;
|
||||
io.File? ssImage;
|
||||
String? dateTime;
|
||||
final globalKey = GlobalKey();
|
||||
|
||||
@@ -184,6 +184,11 @@ class _MapCameraLocationState extends State<MapCameraLocation> {
|
||||
CurrentLocationLayer(
|
||||
alignPositionStream: _followCurrentLocationStreamController.stream,
|
||||
alignPositionOnUpdate: _followOnLocationUpdate,
|
||||
style: LocationMarkerStyle(
|
||||
showHeadingSector: !kIsWeb,
|
||||
showAccuracyCircle: !kIsWeb,
|
||||
),
|
||||
headingStream: kIsWeb ? const Stream.empty() : null,
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -233,18 +238,27 @@ class _MapCameraLocationState extends State<MapCameraLocation> {
|
||||
final rng = Random();
|
||||
final boundary = globalKey.currentContext!.findRenderObject()! as RenderRepaintBoundary;
|
||||
final image = await boundary.toImage(pixelRatio: 2.0);
|
||||
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');
|
||||
|
||||
await imgFile.writeAsBytes(pngBytes);
|
||||
String finalPath = '';
|
||||
|
||||
if (imgFile.existsSync()) {
|
||||
setState(() => cameraImagePath = imgFile);
|
||||
if (kIsWeb) {
|
||||
// On web we use the bytes to create a blob preview or similar
|
||||
// For now we just return an empty path or a fake one to avoid crash
|
||||
finalPath = 'web_capture_${rng.nextInt(10000)}.png';
|
||||
} else {
|
||||
final directory = (await getApplicationDocumentsDirectory()).path;
|
||||
final imgFile = io.File('$directory/screenshot${rng.nextInt(10000)}.png');
|
||||
await imgFile.writeAsBytes(pngBytes);
|
||||
finalPath = imgFile.path;
|
||||
}
|
||||
|
||||
if (finalPath.isNotEmpty) {
|
||||
if (widget.onImageCaptured != null) {
|
||||
widget.onImageCaptured!(ImageAndLocationData(
|
||||
imagePath: imgFile.path,
|
||||
imagePath: finalPath,
|
||||
imageBytes: pngBytes,
|
||||
locationData: locationData,
|
||||
));
|
||||
}
|
||||
@@ -265,6 +279,19 @@ class _MapCameraLocationState extends State<MapCameraLocation> {
|
||||
lngVal = pos.longitude;
|
||||
}
|
||||
|
||||
// Reverse geocoding often doesn't work well on web without a provider
|
||||
if (kIsWeb) {
|
||||
setState(() {
|
||||
locationData = LocationData(
|
||||
latitude: latVal.toString(),
|
||||
longitude: lngVal.toString(),
|
||||
locationName: 'Web Location',
|
||||
subLocation: 'Browser Accuracy',
|
||||
);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
final placeMarks = await placemarkFromCoordinates(latVal, lngVal);
|
||||
|
||||
if (mounted) {
|
||||
@@ -303,37 +330,46 @@ class _MapCameraLocationState extends State<MapCameraLocation> {
|
||||
}
|
||||
|
||||
Future<Position> _determinePosition() async {
|
||||
final serviceEnabled = await Geolocator.isLocationServiceEnabled();
|
||||
if (!serviceEnabled) {
|
||||
final lastKnown = await Geolocator.getLastKnownPosition();
|
||||
if (lastKnown != null) return lastKnown;
|
||||
throw Exception('Location service disabled');
|
||||
// Basic service check
|
||||
if (!kIsWeb) {
|
||||
final serviceEnabled = await Geolocator.isLocationServiceEnabled();
|
||||
if (!serviceEnabled) {
|
||||
final lastKnown = await Geolocator.getLastKnownPosition();
|
||||
if (lastKnown != null) return lastKnown;
|
||||
throw Exception('Location service disabled');
|
||||
}
|
||||
}
|
||||
|
||||
var permission = await Geolocator.checkPermission();
|
||||
if (permission == LocationPermission.denied) {
|
||||
permission = await Geolocator.requestPermission();
|
||||
if (permission == LocationPermission.denied) {
|
||||
final lastKnown = await Geolocator.getLastKnownPosition();
|
||||
if (lastKnown != null) return lastKnown;
|
||||
if (!kIsWeb) {
|
||||
final lastKnown = await Geolocator.getLastKnownPosition();
|
||||
if (lastKnown != null) return lastKnown;
|
||||
}
|
||||
throw Exception('Location permission denied');
|
||||
}
|
||||
}
|
||||
|
||||
if (permission == LocationPermission.deniedForever) {
|
||||
final lastKnown = await Geolocator.getLastKnownPosition();
|
||||
if (lastKnown != null) return lastKnown;
|
||||
if (!kIsWeb) {
|
||||
final lastKnown = await Geolocator.getLastKnownPosition();
|
||||
if (lastKnown != null) return lastKnown;
|
||||
}
|
||||
throw Exception('Location permission permanently denied');
|
||||
}
|
||||
|
||||
try {
|
||||
return await Geolocator.getCurrentPosition(
|
||||
desiredAccuracy: LocationAccuracy.medium,
|
||||
timeLimit: const Duration(seconds: 8),
|
||||
timeLimit: const Duration(seconds: 15),
|
||||
);
|
||||
} catch (e) {
|
||||
final lastKnown = await Geolocator.getLastKnownPosition();
|
||||
if (lastKnown != null) return lastKnown;
|
||||
if (!kIsWeb) {
|
||||
final lastKnown = await Geolocator.getLastKnownPosition();
|
||||
if (lastKnown != null) return lastKnown;
|
||||
}
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user