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 {
|
class ImageAndLocationData {
|
||||||
final String? imagePath;
|
final String? imagePath;
|
||||||
|
final Uint8List? imageBytes;
|
||||||
final LocationData? locationData;
|
final LocationData? locationData;
|
||||||
String? get latitude => locationData?.latitude;
|
String? get latitude => locationData?.latitude;
|
||||||
String? get longitude => locationData?.longitude;
|
String? get longitude => locationData?.longitude;
|
||||||
@@ -8,6 +11,7 @@ class ImageAndLocationData {
|
|||||||
|
|
||||||
ImageAndLocationData({
|
ImageAndLocationData({
|
||||||
required this.imagePath,
|
required this.imagePath,
|
||||||
|
this.imageBytes,
|
||||||
required this.locationData,
|
required this.locationData,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:io';
|
import 'dart:io' as io;
|
||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
|
|
||||||
import 'package:camera/camera.dart';
|
import 'package:camera/camera.dart';
|
||||||
@@ -48,8 +48,8 @@ class _MapCameraLocationState extends State<MapCameraLocation> {
|
|||||||
late AlignOnUpdate _followOnLocationUpdate;
|
late AlignOnUpdate _followOnLocationUpdate;
|
||||||
late StreamController<double?> _followCurrentLocationStreamController;
|
late StreamController<double?> _followCurrentLocationStreamController;
|
||||||
|
|
||||||
File? cameraImagePath;
|
io.File? cameraImagePath;
|
||||||
File? ssImage;
|
io.File? ssImage;
|
||||||
String? dateTime;
|
String? dateTime;
|
||||||
final globalKey = GlobalKey();
|
final globalKey = GlobalKey();
|
||||||
|
|
||||||
@@ -184,6 +184,11 @@ class _MapCameraLocationState extends State<MapCameraLocation> {
|
|||||||
CurrentLocationLayer(
|
CurrentLocationLayer(
|
||||||
alignPositionStream: _followCurrentLocationStreamController.stream,
|
alignPositionStream: _followCurrentLocationStreamController.stream,
|
||||||
alignPositionOnUpdate: _followOnLocationUpdate,
|
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 rng = Random();
|
||||||
final boundary = globalKey.currentContext!.findRenderObject()! as RenderRepaintBoundary;
|
final boundary = globalKey.currentContext!.findRenderObject()! as RenderRepaintBoundary;
|
||||||
final image = await boundary.toImage(pixelRatio: 2.0);
|
final image = await boundary.toImage(pixelRatio: 2.0);
|
||||||
final directory = (await getApplicationDocumentsDirectory()).path;
|
|
||||||
final byteData = await image.toByteData(format: ui.ImageByteFormat.png);
|
final byteData = await image.toByteData(format: ui.ImageByteFormat.png);
|
||||||
final pngBytes = byteData!.buffer.asUint8List();
|
final pngBytes = byteData!.buffer.asUint8List();
|
||||||
final imgFile = File('$directory/screenshot${rng.nextInt(10000)}.png');
|
|
||||||
|
|
||||||
await imgFile.writeAsBytes(pngBytes);
|
String finalPath = '';
|
||||||
|
|
||||||
if (imgFile.existsSync()) {
|
if (kIsWeb) {
|
||||||
setState(() => cameraImagePath = imgFile);
|
// 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) {
|
if (widget.onImageCaptured != null) {
|
||||||
widget.onImageCaptured!(ImageAndLocationData(
|
widget.onImageCaptured!(ImageAndLocationData(
|
||||||
imagePath: imgFile.path,
|
imagePath: finalPath,
|
||||||
|
imageBytes: pngBytes,
|
||||||
locationData: locationData,
|
locationData: locationData,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
@@ -265,6 +279,19 @@ class _MapCameraLocationState extends State<MapCameraLocation> {
|
|||||||
lngVal = pos.longitude;
|
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);
|
final placeMarks = await placemarkFromCoordinates(latVal, lngVal);
|
||||||
|
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
@@ -303,37 +330,46 @@ class _MapCameraLocationState extends State<MapCameraLocation> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<Position> _determinePosition() async {
|
Future<Position> _determinePosition() async {
|
||||||
final serviceEnabled = await Geolocator.isLocationServiceEnabled();
|
// Basic service check
|
||||||
if (!serviceEnabled) {
|
if (!kIsWeb) {
|
||||||
final lastKnown = await Geolocator.getLastKnownPosition();
|
final serviceEnabled = await Geolocator.isLocationServiceEnabled();
|
||||||
if (lastKnown != null) return lastKnown;
|
if (!serviceEnabled) {
|
||||||
throw Exception('Location service disabled');
|
final lastKnown = await Geolocator.getLastKnownPosition();
|
||||||
|
if (lastKnown != null) return lastKnown;
|
||||||
|
throw Exception('Location service disabled');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var permission = await Geolocator.checkPermission();
|
var permission = await Geolocator.checkPermission();
|
||||||
if (permission == LocationPermission.denied) {
|
if (permission == LocationPermission.denied) {
|
||||||
permission = await Geolocator.requestPermission();
|
permission = await Geolocator.requestPermission();
|
||||||
if (permission == LocationPermission.denied) {
|
if (permission == LocationPermission.denied) {
|
||||||
final lastKnown = await Geolocator.getLastKnownPosition();
|
if (!kIsWeb) {
|
||||||
if (lastKnown != null) return lastKnown;
|
final lastKnown = await Geolocator.getLastKnownPosition();
|
||||||
|
if (lastKnown != null) return lastKnown;
|
||||||
|
}
|
||||||
throw Exception('Location permission denied');
|
throw Exception('Location permission denied');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (permission == LocationPermission.deniedForever) {
|
if (permission == LocationPermission.deniedForever) {
|
||||||
final lastKnown = await Geolocator.getLastKnownPosition();
|
if (!kIsWeb) {
|
||||||
if (lastKnown != null) return lastKnown;
|
final lastKnown = await Geolocator.getLastKnownPosition();
|
||||||
|
if (lastKnown != null) return lastKnown;
|
||||||
|
}
|
||||||
throw Exception('Location permission permanently denied');
|
throw Exception('Location permission permanently denied');
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return await Geolocator.getCurrentPosition(
|
return await Geolocator.getCurrentPosition(
|
||||||
desiredAccuracy: LocationAccuracy.medium,
|
desiredAccuracy: LocationAccuracy.medium,
|
||||||
timeLimit: const Duration(seconds: 8),
|
timeLimit: const Duration(seconds: 15),
|
||||||
);
|
);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
final lastKnown = await Geolocator.getLastKnownPosition();
|
if (!kIsWeb) {
|
||||||
if (lastKnown != null) return lastKnown;
|
final lastKnown = await Geolocator.getLastKnownPosition();
|
||||||
|
if (lastKnown != null) return lastKnown;
|
||||||
|
}
|
||||||
rethrow;
|
rethrow;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user