camera_with_gps 2.5.2 copy "camera_with_gps: ^2.5.2" to clipboard
camera_with_gps: ^2.5.2 copied to clipboard

Flutter plugin that captures photos with GPS coordinates embedded in EXIF. Includes pinch zoom, flash, aspect ratio toggle and gallery picker.

camera_with_gps #

pub package pub points likes license

A Flutter plugin that opens a full-screen camera UI and embeds the device's current GPS coordinates into the captured photo's EXIF metadata. Includes pinch-to-zoom, flash, aspect-ratio toggle, and a smart gallery picker.


Features #

  • ๐Ÿ“ธ Full-screen camera UI with shutter, flash, and camera switching.
  • ๐ŸŒ GPS coordinates written straight into the photo's EXIF tags (GPSLatitude, GPSLongitude, GPSLatitudeRef, GPSLongitudeRef).
  • ๐Ÿ” Pinch-to-zoom + preset buttons (0.5x / 1x / 2x), automatically filtered to what the active camera actually supports.
  • ๐Ÿ”ฆ Flash / torch control.
  • ๐Ÿ” Front / back camera switching.
  • ๐Ÿ“ 16:9 โ†” 4:3 aspect-ratio toggle.
  • ๐Ÿ–ผ๏ธ Gallery picker (optional toggle) โ€” uses Android SAF on Samsung devices and the standard image picker elsewhere.
  • โš ๏ธ GPS status banner when location services are disabled or permission is denied โ€” the camera keeps working, GPS is just skipped.
  • ๐Ÿงน Fake-GPS scrubbing โ€” 0,0 coordinates and Samsung's 1970-01-01 placeholder are detected and removed.
  • ๐Ÿ“ฑ Orientation-aware photo rotation via on-device sensors, with platform- specific cropping logic.
  • โšก Low shutter latency โ€” location is pre-warmed in the background and bounded with a 2 s timeout so airplane-mode / cold-start GPS no longer blocks capture.

Installation #

Add the dependency:

dependencies:
  camera_with_gps: ^2.5.0

Then:

flutter pub get

Android #

Minimum SDK: 21. Add to android/app/src/main/AndroidManifest.xml:

<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />

iOS #

Minimum deployment target: 12.0. Add to ios/Runner/Info.plist:

<key>NSCameraUsageDescription</key>
<string>This app needs camera access to take photos with GPS metadata.</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>This app needs location access to add GPS metadata to photos.</string>

Quick start #

import 'package:camera_with_gps/camera_with_gps.dart';
import 'package:flutter/material.dart';

class CapturePage extends StatelessWidget {
  const CapturePage({super.key});

  Future<void> _shoot(BuildContext context) async {
    final path = await CameraWithGps.openCamera(context);
    if (path != null) {
      // ...use the JPEG file at `path`.
    }
  }

  @override
  Widget build(BuildContext context) => Scaffold(
        floatingActionButton: FloatingActionButton(
          onPressed: () => _shoot(context),
          child: const Icon(Icons.camera_alt),
        ),
      );
}

A complete sample lives under example/.

API #

// Open the full-screen camera. `allowGallery` toggles the gallery button.
static Future<String?> openCamera(BuildContext context, {bool allowGallery = true});

// Shortcut: open the camera without the gallery button.
static Future<String?> openCameraPhotoOnly(BuildContext context);

// Pick an existing image (Samsung โ†’ SAF, others โ†’ standard gallery).
static Future<String?> pickFromGallery();

// Write GPS coordinates into the EXIF metadata of an existing JPEG.
static Future<bool> addGps({
  required String path,
  required double latitude,
  required double longitude,
});

// Strip GPS metadata from a JPEG.
static Future<bool> removeGps({required String path});

All Future<String?> results are JPEG file paths (or null on cancel).

Behavior #

GPS attachment. When the shutter is pressed, the plugin tries โ€” in order โ€” (1) the pre-warmed position kept fresh by a background stream, (2) the OS's last-known position, (3) a getCurrentPosition call with a 2 s timeout. If none yield a valid fix, any existing GPS tags on the captured file are scrubbed so the photo never ships with stale or fake coordinates.

Zoom range. Calls getMinZoomLevel() / getMaxZoomLevel() on init and on camera switch. Preset buttons (0.5x, 1x, 2x) appear only when they fall inside that range. On iOS the wide-angle lens reports min == 1.0, so the 0.5x button shows only when the active camera is a different lens. On Android CameraX handles automatic lens switching internally on multi-lens flagships (Pixel 6+, Galaxy S21+), so setZoomLevel(0.6) may transparently engage the ultra-wide camera.

Orientation. The UI is locked to portrait. Photo rotation is computed from sensor orientation (OrientationService) and applied per-platform (PhotoProcessorAndroid / PhotoProcessorIOS).

Samsung handling. Galleries on Samsung devices sometimes drop EXIF GPS or inject placeholder values. The plugin:

  • routes the gallery picker through ACTION_OPEN_DOCUMENT (SAF) on Samsung, which preserves metadata that the standard ImagePicker strips;
  • detects 0, 0 coordinates and the 1970-01-01 date stamp as fake-GPS markers and removes them before returning the path.

Reading the embedded EXIF #

import 'dart:io';
import 'package:exif/exif.dart';

final bytes = await File(path).readAsBytes();
final tags = await readExifFromBytes(bytes);

final gps = {
  for (final e in tags.entries)
    if (e.key.startsWith('GPS')) e.key: e.value.printable,
};

Project layout #

lib/
โ”œโ”€โ”€ camera_with_gps.dart            โ† public library entry (barrel)
โ”‚
โ”œโ”€โ”€ pages/
โ”‚   โ””โ”€โ”€ camera_preview_page.dart    โ† stateful camera shell
โ”‚
โ”œโ”€โ”€ services/
โ”‚   โ”œโ”€โ”€ camera_with_gps.dart        โ† static API class
โ”‚   โ”œโ”€โ”€ orientation_service.dart    โ† sensor-driven orientation stream
โ”‚   โ”œโ”€โ”€ photo_processor.dart        โ† platform facade
โ”‚   โ”œโ”€โ”€ photo_processor_android.dart
โ”‚   โ””โ”€โ”€ photo_processor_ios.dart
โ”‚
โ””โ”€โ”€ widgets/
    โ”œโ”€โ”€ bottom_bar.dart             โ† shutter row + zoom presets slot
    โ”œโ”€โ”€ top_bar.dart                โ† close / flash / ratio
    โ”œโ”€โ”€ preview_box.dart            โ† platform facade for live preview
    โ”œโ”€โ”€ preview_box_android.dart
    โ”œโ”€โ”€ preview_box_ios.dart
    โ”œโ”€โ”€ zoom_presets.dart           โ† 0.5x / 1x / 2x pill
    โ”œโ”€โ”€ shutter_button.dart
    โ”œโ”€โ”€ rot_icon.dart               โ† orientation-aware icon
    โ”œโ”€โ”€ gps_banner.dart
    โ””โ”€โ”€ error_ui.dart

Limitations #

  • The plugin uses the wide-angle back camera by default. On iPhones with multiple physical lenses, switching to ultra-wide or telephoto requires recreating the CameraController with a different CameraDescription returned by availableCameras() โ€” not implemented out of the box.
  • Pure digital zoom only โ€” no built-in support for AVFoundation's builtInTripleCamera smooth lens transitions.
  • Gallery-picked images are returned as-is; the plugin does not try to add GPS data to them. It will only strip fake/placeholder GPS detected via checkGps.

Contributing #

Issues and pull requests are welcome at https://github.com/RuslanMadzhara/camera_with_gps.

  1. Fork the repository.
  2. Create a feature branch: git checkout -b feature/your-feature.
  3. Commit: git commit -m 'Add some feature'.
  4. Push: git push origin feature/your-feature.
  5. Open a Pull Request.

License #

MIT โ€” see LICENSE.

Author #

Ruslan Madzhara ยท LinkedIn

3
likes
140
points
407
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

Flutter plugin that captures photos with GPS coordinates embedded in EXIF. Includes pinch zoom, flash, aspect ratio toggle and gallery picker.

Repository (GitHub)
View/report issues

License

MIT (license)

Dependencies

camera, device_info_plus, flutter, geolocator, image, image_picker, native_device_orientation, plugin_platform_interface

More

Packages that depend on camera_with_gps

Packages that implement camera_with_gps