import 'package:intl/intl.dart'; import 'dart:ui' as ui; import 'package:latlong2/latlong.dart' as lat; import '../../map_camera_flutter.dart'; ///import 'package:your_app/map_camera_flutter.dart'; // Import the file where the MapCameraLocation widget is defined /// ``` /// 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 typedef ImageAndLocationCallback = void Function(ImageAndLocationData data); class MapCameraLocation extends StatefulWidget { final CameraDescription camera; final ImageAndLocationCallback? onImageCaptured; /// 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. const MapCameraLocation( {super.key, required this.camera, this.onImageCaptured}); @override State createState() => _MapCameraLocationState(); } class _MapCameraLocationState extends State { late CameraController _controller; /// Represents a controller for the camera, used to control camera-related operations. late Future _initializeControllerFuture; /// Represents a future that resolves when the camera controller has finished initializing. late AlignOnUpdate _followOnLocationUpdate; /// Enum value indicating when to follow location updates. late StreamController _followCurrentLocationStreamController; /// Stream controller used to track the current location. File? cameraImagePath; /// File path of the captured camera image. File? ssImage; /// File path of the captured screen shot image. String? dateTime; /// A formatted string representing the current date and time. final globalKey = GlobalKey(); /// Key used to uniquely identify and control a widget. Placemark? placeMark; /// Represents geocoded location information. String? latitudeServer; /// Latitude value of the current location as a string. String? longitudeServer; /// Longitude value of the current location as a string. String? locationName; /// Name of the current location as a string. String? subLocation; /// SubLocation of the current location as a string. /// Callback function to retrieve the image and location data. ImageAndLocationData getImageAndLocationData() { return ImageAndLocationData( imagePath: cameraImagePath?.path, latitude: latitudeServer, longitude: longitudeServer, locationName: locationName, subLocation: subLocation, ); } @override void initState() { super.initState(); Timer.periodic(const Duration(seconds: 1), (timer) async { await updatePosition(context); }); // Initialize the camera controller _controller = CameraController( widget.camera, ResolutionPreset.medium, ); _initializeControllerFuture = _controller.initialize(); _followOnLocationUpdate = AlignOnUpdate.always; _followCurrentLocationStreamController = StreamController(); // Get the current date and time in a formatted string dateTime = DateFormat.yMd().add_jm().format(DateTime.now()); } @override void dispose() { _controller.dispose(); _followCurrentLocationStreamController.close(); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( backgroundColor: Colors.black, body: FutureBuilder( future: _initializeControllerFuture, builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.done) { return RepaintBoundary( key: globalKey, child: Stack( children: [ CameraPreview( _controller, ), Positioned( left: 0, right: 0, bottom: 10, child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ SizedBox( height: 160, child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Padding( padding: const EdgeInsets.symmetric(horizontal: 8.0), child: Card( elevation: 3, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8.0)), child: SizedBox( // height: 130, width: 120, child: Padding( padding: const EdgeInsets.all(5.0), child: latitudeServer == null ? const Center( child: CircularProgressIndicator()) : FlutterMap( options: MapOptions( initialCenter: const lat.LatLng(0, 0), initialZoom: 13.0, onPositionChanged: (MapPosition position, bool hasGesture) { if (hasGesture) { setState( () => _followOnLocationUpdate = AlignOnUpdate .never, ); } }, ), children: [ TileLayer( urlTemplate: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png', userAgentPackageName: 'com.example.app', minZoom: 12, ), CurrentLocationLayer( alignPositionStream: _followCurrentLocationStreamController .stream, alignPositionOnUpdate: _followOnLocationUpdate, ), ], ), ), ), ), ), Expanded( child: Container( padding: const EdgeInsets.all(10), decoration: BoxDecoration( borderRadius: BorderRadius.circular(8), color: Colors.black.withOpacity(0.5)), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( locationName ?? "Loading...", maxLines: 1, overflow: TextOverflow.ellipsis, softWrap: false, style: const TextStyle( color: Colors.white, fontSize: 16, fontWeight: FontWeight.bold), ), const SizedBox( height: 5, ), Text( subLocation ?? "Loading ..", maxLines: 2, overflow: TextOverflow.ellipsis, softWrap: false, style: const TextStyle( color: Colors.white, fontSize: 12, fontWeight: FontWeight.w500), ), const SizedBox( height: 5, ), Text( "Lat ${latitudeServer ?? "Loading.."}", maxLines: 2, overflow: TextOverflow.ellipsis, softWrap: false, style: const TextStyle( color: Colors.white, fontSize: 12, fontWeight: FontWeight.w500), ), const SizedBox( height: 5, ), Text( "Long ${longitudeServer ?? "Loading.."}", maxLines: 2, overflow: TextOverflow.ellipsis, softWrap: false, style: const TextStyle( color: Colors.white, fontSize: 12, fontWeight: FontWeight.w500), ), const SizedBox( height: 5, ), Text( dateTime ?? "Loading...", maxLines: 2, overflow: TextOverflow.ellipsis, softWrap: false, style: const TextStyle( color: Colors.white, fontSize: 12, fontWeight: FontWeight.w500), ), ], ), ), ), const SizedBox( width: 10, ) ], ), ), ], ), ), ], ), ); } else { return const Center(child: CircularProgressIndicator()); } }, ), floatingActionButton: FloatingActionButton( onPressed: () async { try { await _initializeControllerFuture; takeScreenshot(); } catch (e) { if (kDebugMode) { print(e); } } }, child: const Icon(Icons.camera_alt), ), ); } /// 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 takeScreenshot() async { var rng = Random(); // Get the render boundary of the widget 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; // 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); // Check if the file exists bool isExists = imgFile.existsSync(); if (isExists) { // Set the file path of the captured image setState(() { cameraImagePath = imgFile; }); // Trigger the image captured callback if (widget.onImageCaptured != null) { ImageAndLocationData data = ImageAndLocationData( imagePath: imgFile.path, latitude: latitudeServer, longitude: longitudeServer, locationName: locationName, subLocation: subLocation, ); 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 updatePosition(BuildContext context) async { try { // Determine the current position final position = await _determinePosition(); // Retrieve the placeMarks for the current position final placeMarks = await placemarkFromCoordinates(position.latitude, position.longitude); if (placeMarks.isNotEmpty) { final placeMark = placeMarks.first; // Update the state variables with the retrieved location data setState(() { latitudeServer = position.latitude.toString(); longitudeServer = position.longitude.toString(); locationName = "${placeMark.locality ?? ""}, ${placeMark.administrativeArea ?? ""}, ${placeMark.country ?? ""}"; subLocation = "${placeMark.street ?? ""}, ${placeMark.thoroughfare ?? ""} ${placeMark.administrativeArea ?? ""}"; }); if (kDebugMode) { print( "Latitude: $latitudeServer, Longitude: $longitudeServer, Location: $locationName"); } } else { // Handle case when no placeMark is available setState(() { latitudeServer = null; longitudeServer = null; locationName = 'No Location Data'; subLocation = ''; }); } } catch (e) { // Handle any errors that occurred during location retrieval setState(() { latitudeServer = null; longitudeServer = null; locationName = 'Error Retrieving Location'; subLocation = ''; }); } } /// 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 _determinePosition() async { bool serviceEnabled; LocationPermission permission; // Check if location services are enabled serviceEnabled = await Geolocator.isLocationServiceEnabled(); if (!serviceEnabled) { // If location services are disabled, throw an exception throw Exception('Location services are disabled.'); } // Check location permission permission = await Geolocator.checkPermission(); if (permission == LocationPermission.denied) { // If location permission is denied, request it permission = await Geolocator.requestPermission(); if (permission == LocationPermission.denied) { // If location permission is still denied, throw an exception throw Exception('Location permissions are denied'); } } // Check if location permission is permanently denied if (permission == LocationPermission.deniedForever) { // Throw an exception if location permission is permanently denied throw Exception( 'Location permissions are permanently denied, we cannot request permissions.'); } // Get the current position return await Geolocator.getCurrentPosition( desiredAccuracy: LocationAccuracy.best, ); } }