feat: implement map camera widget with integrated location data capture and display

This commit is contained in:
2026-04-02 13:11:38 +07:00
parent e5e55deeef
commit f6d80613d9
2 changed files with 61 additions and 21 deletions

View File

@@ -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,
});
}

View File

@@ -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;
}
}