# How to Add Geolocation to Your App (Copy-Paste Prompts Included)

- Tool: App Features
- Last updated: July 2026

## TL;DR

Geolocation needs a permission request layer, a location provider (Geolocation API on web, geolocator package in Flutter), and a Supabase table with PostGIS for storing and querying coordinates. With Lovable or FlutterFlow you can ship a working implementation in 3–6 hours for $0–$5/month up to 1,000 users. The only costs that appear are reverse geocoding (converting coordinates to addresses) and Supabase Pro beyond the free tier.

## What Geolocation Actually Is in an App Context

Geolocation gives your app the user's physical coordinates — latitude, longitude, and accuracy radius. On web it calls navigator.geolocation.getCurrentPosition(); on Flutter it uses the geolocator package. Those coordinates then power everything else: centering a map on the user, filtering nearby listings, checking in at a location, or querying which delivery zone someone is in. The positioning itself is a solved, free problem. The product decisions that matter are accuracy versus battery life, what to do when the user says no, and how to store coordinates so PostGIS can answer proximity queries like "find listings within 5km" without a full table scan.

## Anatomy of the Geolocation Feature

Five components — two of which AI tools generate reliably, two that commonly break, and one (the reverse geocoder) that requires an Edge Function to hide the API key.

- **Permission request layer** (ui): Prompts the user for location permission before any position read. On web this is the browser's native permission dialog triggered by navigator.geolocation.getCurrentPosition(). On Flutter, the geolocator package combined with permission_handler (pub.dev) handles the request and auto-wires AndroidManifest.xml and Info.plist entries.
- **Location provider** (backend): Reads the actual coordinates from GPS, Wi-Fi triangulation, and cell towers. On web: navigator.geolocation.getCurrentPosition({ enableHighAccuracy: true, timeout: 10000 }). On Flutter: geolocator.getCurrentPosition(desiredAccuracy: LocationAccuracy.high) for precise features, LocationAccuracy.low for background or battery-sensitive use cases.
- **Position store** (data): A Supabase table user_locations with columns user_id, lat, lng, accuracy, and recorded_at. Storing coordinates enables history screens, proximity queries via PostGIS ST_DWithin, and audit trails for deliveries or field workers.
- **Reverse geocoder** (service): Converts raw lat/lng coordinates into a human-readable address string ('123 Main St, Austin, TX'). Options: Google Maps Geocoding API ($5 per 1,000 requests after $200/mo free credit) or Mapbox Geocoding API (100,000 requests/mo free, then $0.75 per 1,000 approx). Always called via a Supabase Edge Function — never directly from the client — to keep the API key secret.
- **Permission state manager** (ui): Tracks the three possible states: granted, denied, and undetermined (not yet asked). Renders the appropriate UI for each: the map or nearby content for granted, a manual city/address input picker for denied, and a permission request button for undetermined. On Flutter, the permission_handler package exposes a PermissionStatus enum covering all three states.

## Data model

One table captures location history with full PostGIS support for proximity queries. Run this in the Supabase SQL editor after enabling the PostGIS extension:

```sql
-- Enable PostGIS (run once per project)
CREATE EXTENSION IF NOT EXISTS postgis;

create table public.user_locations (
  id uuid primary key default gen_random_uuid(),
  user_id uuid references auth.users(id) on delete cascade not null,
  lat float8 not null,
  lng float8 not null,
  accuracy float4,
  source text default 'gps',
  recorded_at timestamptz not null default now()
);

alter table public.user_locations enable row level security;

create policy "Users can view own locations"
  on public.user_locations for select
  using (auth.uid() = user_id);

create policy "Users can insert own locations"
  on public.user_locations for insert
  with check (auth.uid() = user_id);

create index user_locations_user_idx
  on public.user_locations (user_id, recorded_at desc);

create index user_locations_geo_idx
  on public.user_locations using gist (
    st_point(lng, lat)::geography
  );
```

The GiST index on the geography column is what makes ST_DWithin proximity queries fast at scale. Without it, a 'find users within 5km' query does a full table scan. The source column lets you distinguish GPS fixes from manual city picker entries — useful for analytics on accuracy quality.

## Build paths

### Lovable — fit 3/10, 3–5 hours

Lovable generates the permission flow, Supabase insert, and manual fallback in one prompt — but the preview iframe silently blocks navigator.geolocation, so you must publish to test any location logic.

1. Create a Lovable project with Lovable Cloud enabled so Supabase auth and the locations table are provisioned
2. Add the MAPBOX_PUBLIC_TOKEN or GOOGLE_GEOCODING_KEY to the Secrets panel in the Cloud tab if you want reverse geocoding
3. Paste the prompt below and let Agent Mode build the permission flow, location read, Supabase insert, and denied-state fallback
4. Click Publish and test the live URL on your phone over HTTPS — geolocation never fires inside the desktop preview iframe

Starter prompt:

```
Build a geolocation feature for this app. Use navigator.geolocation.getCurrentPosition with enableHighAccuracy: true and a 10-second timeout. Show three states: (1) 'undetermined' — a centered button 'Allow location access' that triggers the permission prompt; (2) 'acquiring' — a spinner with text 'Acquiring your location…'; (3) 'granted' — show the user's lat/lng and accuracy in metres, then insert a row into a Supabase table called user_locations (user_id from auth, lat, lng, accuracy float, source 'web', recorded_at now()). For state (4) 'denied' — show a manual city input field (text input + search button) so the user can still use the app. Apply RLS: users can only read and insert their own rows. Accuracy threshold: ignore readings worse than 500 metres (accuracy > 500) and retry once. Do not show the location to other users.
```

Limitations:

- navigator.geolocation silently returns nothing inside the Lovable preview iframe — publish before testing any location logic
- Reverse geocoding (lat/lng to address) requires an additional Edge Function to hide the API key; the first-generation prompt output rarely includes this
- FlutterFlow is a better choice for native mobile accuracy — web geolocation via Lovable uses browser APIs, which are less precise than on-device GPS

### Flutterflow — fit 5/10, 3–6 hours

FlutterFlow's geolocator integration is first-class — the Action editor wires permission requests, accuracy settings, and AndroidManifest/Info.plist entries automatically, making this the best path for any mobile-first geolocation feature.

1. In the Action editor, add a 'Request Permission' action and select Location; FlutterFlow auto-adds ACCESS_FINE_LOCATION and ACCESS_COARSE_LOCATION to AndroidManifest.xml and NSLocationWhenInUseUsageDescription to Info.plist
2. Add a 'Get Current Location' action (wraps geolocator.getCurrentPosition) after the permission grant; store the result in a page state variable of type LatLng
3. Wire conditional visibility: show the spinner widget while location is null, show the location display once LatLng is populated, and show the manual input widget when permission is denied (check PermissionStatus via permission_handler)
4. Add a Supabase Insert action writing user_id (from Supabase Auth), lat, lng, accuracy, and recorded_at to the user_locations table
5. Test on a physical device via the FlutterFlow mobile app — the web preview does not access native GPS

Limitations:

- Background location tracking (rides, deliveries) requires a custom code block with Workmanager; FlutterFlow's built-in action reads location once on tap
- PostGIS ST_DWithin proximity queries require a Supabase custom RPC function called via the REST API; FlutterFlow cannot build the SQL query visually
- App Store submission requires a specific NSLocationWhenInUseUsageDescription string — 'we need location' phrasing causes rejection; be specific

### V0 — fit 2/10, 4–6 hours

V0 generates a clean permission UI and a geocoding API route, but geolocation on web is degraded compared to native mobile — this path suits web-only apps where location is a supporting feature, not the core product.

1. Prompt V0 to generate a geolocation component with the three permission states and a manual fallback
2. Add your geocoding API key in the Vars panel as NEXT_PUBLIC_MAPBOX_TOKEN (for client-safe reverse geocoding) or wire an API route for the secret key
3. Run the Supabase SQL schema in the Supabase SQL editor and add connection env vars in the Vars panel
4. Deploy and test on HTTPS — browser geolocation does not fire in V0's sandbox preview

Starter prompt:

```
Create a Next.js geolocation component using the browser Geolocation API. Three rendered states: (1) 'idle' — a button 'Share my location' that calls navigator.geolocation.getCurrentPosition with enableHighAccuracy: true and a 10s timeout; (2) 'loading' — spinner with 'Acquiring location…' text; (3) 'success' — show lat/lng and accuracy in metres; (4) 'denied' — show a city search input as fallback. On success, POST to /api/location with { lat, lng, accuracy } which inserts into Supabase user_locations table (auth user_id, lat, lng, accuracy, recorded_at). Ignore readings with accuracy > 500m and retry once. All Supabase calls use the service role key server-side only.
```

Limitations:

- Web geolocation via browser APIs is less accurate than native GPS — cell-tower-only fixes can be 1–5km off in urban areas
- V0 is web-only; no native mobile path; FlutterFlow is the right tool for mobile-first geolocation
- V0 sandbox preview blocks geolocation — deploy to a Vercel URL and test on a real device

### Custom — fit 5/10, 3–5 days

Custom development adds background location tracking, geofencing, PostGIS proximity indexing for millions of records, and accuracy below 10m — required for rides, deliveries, field service, or precision navigation.

1. Flutter: geolocator package for foreground position + Workmanager for background tracking; configure Isolate and wake-lock handling for Android Doze mode compatibility
2. PostGIS GiST index on a geography column enables ST_DWithin queries across millions of rows in under 50ms
3. Geofencing via flutter_geofence package — define polygons, receive enter/exit callbacks, fire push notifications via Supabase Edge Function
4. Supabase RPC functions expose proximity search as a REST endpoint, keeping query logic server-side and away from client code

Limitations:

- Background location on iOS requires the Location background mode capability and App Store justification — plan 2–4 weeks extra for review
- Accuracy below 10m (sports, precision navigation) requires external GPS hardware integration beyond what mobile APIs expose

## Gotchas

- **Lovable preview returns no location — silently** — The browser security policy denies geolocation to cross-origin iframes. The Lovable preview pane is embedded in an iframe, so navigator.geolocation.getCurrentPosition() executes but the success callback never fires — no error, no result, no indication anything is wrong. Fix: Always test geolocation on the published production URL, not the preview. In Lovable, click Publish and open the live URL on your phone. Add a temporary lat/lng override constant (e.g. lat: 40.7128, lng: -74.006) during development so you can verify the rest of the feature before testing the actual GPS read.
- **Permission denied shows a blank screen with no fallback** — AI-generated code typically checks navigator.permissions or the error code in the getCurrentPosition error callback, but renders nothing when the user denies. The user is left staring at an empty div with no way to proceed. Fix: Explicitly prompt for a manual city/address input fallback that renders when permissionDenied state is true. The component should have four states in code: idle, loading, success, and denied — each with distinct UI. Never leave denied as a blank render.
- **FlutterFlow crashes on Android 12+ without COARSE location permission** — The geolocator package requires both ACCESS_FINE_LOCATION and ACCESS_COARSE_LOCATION in AndroidManifest.xml on Android 12+. When only FINE is declared, the permission dialog sometimes fails silently and the app crashes on position read. Fix: In FlutterFlow, go to Settings → App Permissions and explicitly enable both Fine Location and Coarse Location. FlutterFlow sometimes omits COARSE when only FINE is requested in the Action editor. Verify both appear in the generated AndroidManifest.xml before building a release APK.
- **Supabase INSERT exposes all user locations without RLS** — AI-generated code often creates the user_locations table and inserts rows correctly but skips enabling Row Level Security. Anyone with the anon key can then run SELECT * FROM user_locations and see every user's coordinates — a serious privacy violation. Fix: Enable RLS on user_locations immediately after creating the table. Add two policies: SELECT USING (auth.uid() = user_id) and INSERT WITH CHECK (auth.uid() = user_id). Test by querying the table with a different user's session to confirm rows are invisible.
- **GPS cold start takes 30–60 seconds with no feedback** — On the first position read after a device restart, GPS needs time to acquire satellite lock. navigator.geolocation defaults to returning a cached position that may be hours old and kilometres off. geolocator on Flutter returns a cached fix immediately with low accuracy. If the UI transitions to 'success' on the first result without checking accuracy, it shows the wrong location. Fix: Always check the accuracy value on the first position result. Set a threshold (e.g. reject readings with accuracy > 100m) and continue showing the 'acquiring' state with a progress message until a sufficiently accurate fix arrives. Set a forceAndroidLocationManager: false flag in Flutter and pass a timeout so the app doesn't wait indefinitely.

## Best practices

- Request location permission from a clear user action, not on page load — explaining why before the prompt doubles acceptance rates
- Cache the last known good position in Supabase and display it immediately on relaunch while a fresh fix acquires
- Always build the manual city/address input fallback before shipping — denying location permission is common on mobile, especially on first launch
- Use low accuracy mode (cell + Wi-Fi) for non-precision features like region detection — it responds in under a second and drains far less battery
- Geocode addresses once and cache the result in your database — re-geocoding the same coordinates on every request wastes API quota and adds latency
- Log accuracy values alongside coordinates — knowing whether your users are getting GPS-quality fixes or cell-tower guesses helps diagnose proximity feature failures
- Never hardcode geocoding API keys in frontend code — always proxy through a Supabase Edge Function using Deno.env.get()
- Enable the PostGIS GiST index before you need it — retrofitting it on a table with millions of rows locks the table and causes downtime

## Frequently asked questions

### Does geolocation work in Lovable preview?

No. The Lovable preview pane runs inside a cross-origin iframe, which browsers block from accessing geolocation for security reasons. navigator.geolocation.getCurrentPosition() executes silently with no result and no error. Publish your app and test on the live HTTPS URL from a real phone — geolocation works correctly there.

### How do I store user locations in Supabase?

Create a user_locations table with columns for user_id (FK to auth.users), lat (float8), lng (float8), accuracy (float4), and recorded_at (timestamptz). Enable Row Level Security and add a policy so users can only read and insert their own rows. For proximity queries, enable the PostGIS extension and add a GiST index on the geography column — this makes ST_DWithin queries fast even across millions of rows.

### What happens when a user denies location permission?

Your app should immediately show a manual city or address input field — never a blank screen. Store the user's manually entered location the same way you'd store a GPS result, just with a different source value. On web, after a user denies, the browser often blocks future permission prompts from the same origin, so the fallback is the only path back.

### How accurate is web geolocation compared to native mobile?

Web geolocation (navigator.geolocation) uses whatever signals the browser can access — GPS if the device has it and permission is granted, then Wi-Fi positioning, then cell tower triangulation. In practice, a phone browser with GPS on achieves 5–15m accuracy, similar to native. The key difference is that native apps (via geolocator in Flutter) can access background location and continuous updates, while web can only read location when the page is in the foreground.

### How do I convert coordinates to an address?

Use a reverse geocoding API: Mapbox Geocoding API is the best free option (100K requests/mo free), and Google Maps Geocoding API offers $200/mo credit then $5/1K requests. Always call the geocoding API from a server-side function (Supabase Edge Function or Next.js API route) rather than directly from the client — your API key would be exposed in browser network requests otherwise. Cache every result in your database to avoid repeat calls.

### Is location data stored securely?

Only if you enable Supabase Row Level Security. Without RLS, anyone with your anon key can query every user's coordinates. Enable RLS on user_locations and set a policy of USING (auth.uid() = user_id) — this ensures each user can only access their own location history. Never store precise GPS trails without user consent disclosure in your privacy policy.

### Can I track location in the background on mobile?

Yes, but it requires custom development beyond what AI tools generate. On Flutter, Workmanager handles background Dart isolates; on iOS, you need the Location background mode capability and a convincing App Store justification. Background location is the feature most likely to trigger App Store review requests or rejection — budget an extra 2–4 weeks for that process.

### How much does reverse geocoding cost at scale?

Mapbox gives 100,000 geocoding requests per month free — enough for roughly 3,000 daily active users who each trigger one geocode per session. Beyond that, $0.75 per 1,000 requests (approx). The way to keep costs near zero is to cache: store the geocoded address in your database the first time, and serve the cached version for all future reads of the same coordinates.

---

Source: https://www.rapidevelopers.com/app-features/geolocation
© RapidDev — https://www.rapidevelopers.com/app-features/geolocation
