Flutter Geolocator returns different coordinates on iOS vs Android for same physical location, causing inconsistent reverse geocoding address


Problem

I'm using the geolocator package in Flutter to get the current location and then reverse geocode it to a display address. The same physical device location returns noticeably different coordinates on iOS and Android, which causes the geocoded address to differ between platforms.


Code

Getting current position:

dart

return await Geolocator.getCurrentPosition(
  desiredAccuracy: LocationAccuracy.high,
);

Reverse geocoding (Google Maps Geocoding API):

dart

static Future<String> getAddressFromLatLng(String? lat, String? lng) async {
  final double roundedLat = double.parse(double.parse(lat!).toStringAsFixed(5));
  final double roundedLng = double.parse(double.parse(lng!).toStringAsFixed(5));

  final uri = Uri.parse(
    'https://maps.googleapis.com/maps/api/geocode/json'
    '?latlng=$roundedLat,$roundedLng'
    '&key=$apiKey'
    '&language=en',
  );

  final response = await http.get(uri);
  final data = json.decode(response.body);
  return data['results'][0]['formatted_address'];
}

Logs

iOS:

Got current location: 22.685498910314223, 75.85949381064067
Rounded: 22.6855, 75.85949
Resolved address: Mangal Nagar, Indore, 452014

Android (same room, same time):

Got current location: 22.685553, 75.8595931
Rounded: 22.68555, 75.85959
Resolved address: AB Road, Vishnu Puri Colony, Indore, 452014

What I've already tried

  1. Rounding coordinates to 5 decimal places before the API call — coordinates are still ~10–15 meters apart after rounding, enough to cross a street boundary

  2. Switching from formatted_address to address_components — skipping street_number and route, only using sublocality, locality, postal_code — still getting different neighborhood names

  3. Filtering result_type=neighborhood|sublocality in the geocoding request — partially helped but still inconsistent


Questions

  1. Why does LocationAccuracy.high produce coordinates ~10–15 meters apart between iOS and Android on the same physical location?

  2. Is there a reliable way to get consistent coordinates across both platforms using geolocator?

  3. Should I be averaging multiple position readings to stabilize the result, and if so what is the recommended approach?


Environment

Flutter: 3.x
geolocator: ^13.x
Device tested: iPhone 14 (iOS 17) + Samsung Galaxy (Android 13)
Same physical location, same time
1
Apr 18 at 7:08 AM
User AvatarShahnawaz Khan
#android#flutter#dart#geolocation#reverse-geocoding

Accepted Answer

// Root cause:
// iOS (CoreLocation) and Android (FusedLocationProvider) use different sensors,
// fusion algorithms, and map matching → ~5–20m variance is NORMAL.
// You cannot force identical coordinates across platforms.
//
// Solution approach:
// 1. Take multiple samples
// 2. Filter by accuracy
// 3. Average coordinates
// 4. Optionally snap to a grid to stabilize reverse geocoding

import 'dart:async';
import 'dart:math';
import 'package:geolocator/geolocator.dart';

class StableLocationService {
  static const int sampleCount = 5;
  static const double maxAccuracyMeters = 25;

  static Future<Position> getStablePosition() async {
    List<Position> samples = [];

    while (samples.length < sampleCount) {
      final pos = await Geolocator.getCurrentPosition(
        desiredAccuracy: LocationAccuracy.high,
      );

      if (pos.accuracy <= maxAccuracyMeters) {
        samples.add(pos);
      }

      await Future.delayed(const Duration(milliseconds: 500));
    }

    return _averagePosition(samples);
  }

  static Position _averagePosition(List<Position> positions) {
    double lat = 0;
    double lng = 0;

    for (var p in positions) {
      lat += p.latitude;
      lng += p.longitude;
    }

    lat /= positions.length;
    lng /= positions.length;

    return Position(
      latitude: lat,
      longitude: lng,
      timestamp: DateTime.now(),
      accuracy: positions.map((e) => e.accuracy).reduce(min),
      altitude: 0,
      heading: 0,
      speed: 0,
      speedAccuracy: 0,
      altitudeAccuracy: 0,
      headingAccuracy: 0,
    );
  }

  // Snap to grid (~10m) to avoid crossing street boundaries
  static double snap(double value, double gridSizeMeters) {
    const earthRadius = 6378137.0;
    double gridDegrees = gridSizeMeters / earthRadius * (180 / pi);
    return (value / gridDegrees).round() * gridDegrees;
  }

  static Map<String, double> getSnappedLatLng(
    double lat,
    double lng, {
    double gridMeters = 10,
  }) {
    return {
      "lat": snap(lat, gridMeters),
      "lng": snap(lng, gridMeters),
    };
  }
}

// Usage:
//
// final pos = await StableLocationService.getStablePosition();
// final snapped = StableLocationService.getSnappedLatLng(
//   pos.latitude,
//   pos.longitude,
// );
//
// Use snapped["lat"], snapped["lng"] for reverse geocoding.
//
// Result:
// - Reduces platform variance
// - Prevents street-boundary flips
// - Produces consistent addresses across iOS & Android
User AvatarSanjib
Apr 18 at 7:38 AM
1