# OpenStreetMap

- Tool: FlutterFlow
- Difficulty: Intermediate
- Time required: 45 minutes
- Last updated: July 2026

## TL;DR

Connect FlutterFlow to OpenStreetMap by building a Custom Widget that wraps the flutter_map pub.dev package with an OSM TileLayer — FlutterFlow's built-in map widget only supports Google Maps, so a Custom Widget is mandatory for OSM. Add address search by creating an API Call to the Nominatim geocoding endpoint with a valid User-Agent header. Respect OSM's tile usage policy: set a real User-Agent and use a paid tile host for production traffic.

## OpenStreetMap in FlutterFlow: Free Maps Without Google or API Keys

OpenStreetMap (OSM) is the Wikipedia of maps — a community-maintained, globally complete map dataset that is free to use for rendering map tiles, geocoding addresses, and building location-aware apps. Unlike Google Maps or Mapbox, OSM has no API key and no billing dashboard. The map tiles are served for free from the OSM tile infrastructure at tile.openstreetmap.org, and address search is available through the Nominatim geocoding service at nominatim.openstreetmap.org. For light-use apps and development, this is completely free.

For FlutterFlow developers, the key reality is that the platform's built-in Google Map widget cannot display OpenStreetMap tiles — these are entirely different rendering systems. To show an OSM map in a FlutterFlow app you must build a Custom Widget using the flutter_map Dart package (available on pub.dev), which is a mature, well-maintained library designed specifically for rendering map tiles including OSM. You write the widget once, expose center/zoom/markers as widget parameters, and then use it like any other widget on your FlutterFlow pages.

One important caveat: OSM has a usage policy. The public tile servers and Nominatim are community resources and are not designed for production-scale traffic. For a hobby project or internal tool, the free tier is fine. For any app with more than a handful of active users, you should either self-host tile rendering or use a paid tile provider such as MapTiler or Thunderforest that is built on OSM data but can handle production request volumes. Always set a descriptive User-Agent header on Nominatim requests — requests without a valid User-Agent are blocked automatically.

## Before you start

- A FlutterFlow project — Free plan supports Custom Widgets but you will need Run mode or a device build to see the map render
- Basic familiarity with FlutterFlow's Custom Code section (no advanced Dart experience required — the full widget code is provided below)
- An understanding that the OSM map will show a placeholder box in the canvas editor — this is expected behavior; verify on a real device
- For production apps: a MapTiler or Thunderforest account for a tile URL that can handle real user traffic
- A real Android or iOS device or an emulator/simulator for testing the rendered map widget

## Step-by-step guide

### 1. Add flutter_map and latlong2 as Custom Code dependencies

In FlutterFlow, click Custom Code in the left navigation panel. In the Custom Code settings area, find the Dependencies section (sometimes labeled 'pub.dev Packages'). Click Add Dependency. Type flutter_map and add the latest stable version — at the time of writing this is 7.x, but check pub.dev/packages/flutter_map for the current release. Click Add again and add latlong2, which provides the LatLng type that flutter_map uses for coordinates.

Do not type 'flutter pub add' or 'pub get' — FlutterFlow handles all dependency resolution automatically when you add packages through the Custom Code UI. After adding both packages, FlutterFlow will show them in the dependency list. If a version conflict indicator appears (a yellow or red warning), try the previous minor version of flutter_map (e.g., 6.x) — version pinning conflicts are one of the most common FlutterFlow Custom Code issues.

Wait for FlutterFlow to sync the dependencies before moving to the next step. If the sync fails, try removing and re-adding flutter_map — sometimes a fresh addition resolves the conflict.

**Expected result:** Both flutter_map and latlong2 appear in your FlutterFlow Custom Code dependencies list without version conflict warnings.

### 2. Create the OSM Custom Widget in FlutterFlow

In the Custom Code section, click + Add → Widget. Name it 'OSMMapWidget'. FlutterFlow will open a code editor with a template widget. Replace the template with the complete flutter_map widget below.

The widget accepts five parameters that you will configure in the widget's Parameters section in FlutterFlow: latitude (double, center latitude), longitude (double, center longitude), zoom (double, initial zoom level), and markerLatLngs (a list of strings in 'lat,lng' format that you will build from your data). Set sensible defaults: latitude 51.5074, longitude -0.1278 (London), zoom 13.0.

After pasting the code, click the Parameters tab in the Custom Widget editor and add each parameter with its type. FlutterFlow will then let you bind these parameters to Page State variables, API Call response values, or hardcoded values when you place the widget on a page.

The Custom Widget will show a grey placeholder box in the FlutterFlow canvas editor. This is expected — flutter_map requires a real rendering context and will only display in Run mode or on a device.

```
import 'package:flutter/material.dart';
import 'package:flutter_map/flutter_map.dart';
import 'package:latlong2/latlong.dart';

class OSMMapWidget extends StatefulWidget {
  const OSMMapWidget({
    super.key,
    this.width,
    this.height,
    this.latitude = 51.5074,
    this.longitude = -0.1278,
    this.zoom = 13.0,
    this.markerLatLngs = const [],
  });

  final double? width;
  final double? height;
  final double latitude;
  final double longitude;
  final double zoom;
  final List<String> markerLatLngs; // each item: "lat,lng"

  @override
  State<OSMMapWidget> createState() => _OSMMapWidgetState();
}

class _OSMMapWidgetState extends State<OSMMapWidget> {
  @override
  Widget build(BuildContext context) {
    final markers = widget.markerLatLngs.map((item) {
      final parts = item.split(',');
      if (parts.length != 2) return null;
      final lat = double.tryParse(parts[0].trim());
      final lng = double.tryParse(parts[1].trim());
      if (lat == null || lng == null) return null;
      return Marker(
        point: LatLng(lat, lng),
        width: 40,
        height: 40,
        child: const Icon(Icons.location_pin, color: Colors.red, size: 40),
      );
    }).whereType<Marker>().toList();

    return SizedBox(
      width: widget.width ?? double.infinity,
      height: widget.height ?? 300,
      child: FlutterMap(
        options: MapOptions(
          initialCenter: LatLng(widget.latitude, widget.longitude),
          initialZoom: widget.zoom,
        ),
        children: [
          TileLayer(
            urlTemplate: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
            userAgentPackageName: 'com.example.myapp', // replace with your app ID
          ),
          MarkerLayer(markers: markers),
        ],
      ),
    );
  }
}
```

**Expected result:** The OSMMapWidget appears in your FlutterFlow Custom Widgets list. It shows a grey placeholder in the canvas but renders a live OSM map in Run mode and on a real device.

### 3. Place the OSM widget on a page and bind parameters to Page State

Create a new page in your FlutterFlow project (or use an existing one) and name it something like 'MapScreen'. Add two Page State variables: mapLat (double, default 51.5074), mapLng (double, default -0.1278), and mapMarkers (a list of Strings, default empty list). These will drive the Custom Widget.

From the Widget Palette, find your OSMMapWidget under Custom Widgets and drag it onto the page. Set its width to the full page width (use the 'Expand' option or set percentage to 100%) and its height to an appropriate value such as 400 px or half the screen height.

In the widget's parameter bindings, bind latitude to the mapLat Page State variable, longitude to the mapLng Page State variable, and markerLatLngs to the mapMarkers Page State variable. For now, hardcode a default zoom value of 13.0.

Above the map widget, add a TextField for address search and a Button labeled 'Search'. The button will trigger the Nominatim API Call you will build in the next step. As a quick test, you can also add a button that sets mapLat to 48.8566 and mapLng to 2.3522 (Paris) — in Run mode, the map should pan to Paris when you tap it. This confirms the widget parameter binding works correctly before you add the geocoding step.

**Expected result:** The OSMMapWidget is placed on the page and correctly responds to latitude and longitude changes via Page State in Run mode.

### 4. Create the Nominatim geocoding API Call

Click API Calls in the left nav, then click + Add → Create API Call. Name it 'NominatimSearch'. Set Method to GET and URL to https://nominatim.openstreetmap.org/search. In the Query Parameters tab, add three parameters: q (the address string, bound to the variable searchQuery), format with hardcoded value json, and limit with hardcoded value 5.

The critical requirement for Nominatim is the User-Agent HTTP header. In the Headers tab, add a header with Key = User-Agent and Value = YourAppName/1.0 (yourcontact@email.com). Nominatim's usage policy requires a meaningful User-Agent identifying your application and contact. Requests without a valid User-Agent, or using a generic browser UA, will receive HTTP 403 responses. Replace the example value with your actual app name and a contact email.

In the Response & Test tab, enter a test address such as 'Eiffel Tower, Paris' in the searchQuery field and click Test. You should see a JSON array where each item has lat, lon, and display_name fields. Click Generate JSON Paths to extract $.0.lat, $.0.lon, and $.0.display_name — these are the values you will use to update your Page State map center.

Note that Nominatim rate-limits requests to approximately one per second and prohibits bulk usage or high-frequency calls on the public servers. For a search-as-you-type autocomplete UI, add at minimum a 500ms debounce delay before firing the API Call.

```
// Nominatim API Call config (reference for FlutterFlow UI entry)
// Method: GET
// URL: https://nominatim.openstreetmap.org/search
// Query params:
//   q = {{searchQuery}}
//   format = json
//   limit = 5
// Headers:
//   User-Agent: YourAppName/1.0 (your@email.com)
// Response JSON Paths:
//   First result latitude:  $.0.lat
//   First result longitude: $.0.lon
//   First result name:      $.0.display_name
```

**Expected result:** The NominatimSearch API Call returns a JSON array with lat, lon, and display_name for a test address. JSON Paths are generated and ready to use in your action flow.

### 5. Wire the search action and add geocoding results as map markers

On your MapScreen page, select the Search button and open the Action Flow Editor. Add an API Call action that calls NominatimSearch with the TextField's value as the searchQuery variable. After the API Call, add a conditional: if the response has at least one result (check that JSON Path $.0.lat is not empty), proceed; otherwise show a Snackbar with 'Address not found'.

If the result is valid, add two Set Page State actions: one that sets mapLat to the value at JSON Path $.0.lat (converting from String to Double using a code expression or the built-in String to Number conversion), and one that sets mapLng to $.0.lon similarly. This will re-center the OSM Custom Widget on the geocoded location.

To add a marker pin at the searched location, build a string in the format 'lat,lng' and add it to the mapMarkers Page State list using an Add to List action. The Custom Widget will parse this string and render a red pin at that coordinate.

For a production app, consider upgrading the Nominatim tile requests to a paid OSM-based tile provider such as MapTiler (which provides an OSM-compatible tile URL with an API key) — replace the urlTemplate in the Custom Widget with the MapTiler URL and store the key in App State. This keeps the free OSM map style while using infrastructure that can handle production traffic.

**Expected result:** Entering an address and tapping Search geocodes it via Nominatim, pans the OSM map to that location, and places a red pin marker at the result.

### 6. Test on a real device and review OSM usage policy compliance

Custom Widgets in FlutterFlow render only in Run mode (the full-screen test view) and on real device or emulator builds — they display as grey placeholder boxes in the canvas editor. Switch to Run mode using the Run button at the top right of FlutterFlow to see the OSM map render for the first time. If the map tiles appear but are very slow to load, you may be hitting Nominatim or OSM tile rate limits — this is normal in a development environment.

Before publishing your app, review the OSM tile usage policy at operations.osmfoundation.org/policies/tiles. Key requirements: do not hard-refresh tiles on every app open (cache tiles between sessions), set a meaningful User-Agent in your TileLayer's userAgentPackageName, and do not use the public tile server for apps with heavy traffic. For commercial apps or apps with more than a few hundred daily active users, switch to a paid tile provider that serves OSM-based tiles at scale — MapTiler has a generous free tier and is OSM-compatible, so your Custom Widget tile URL is the only thing that needs to change.

For Nominatim, the limit is roughly one geocoding request per second per application. For search-as-you-type, add a debounce so you only fire the API Call after the user stops typing for 500ms. For high-volume geocoding (e.g., geocoding a list of addresses on app load), Nominatim prohibits bulk use — use a paid geocoding API instead.

**Expected result:** The OSM map renders correctly in Run mode and on a real device, tiles load within a few seconds, and the Nominatim search correctly moves the map center and places markers.

## Best practices

- Always set userAgentPackageName in your flutter_map TileLayer to your real app bundle ID — requests with no User-Agent are blocked by Nominatim and violate OSM's tile usage policy
- Use a paid OSM tile provider (MapTiler, Thunderforest) for production apps with real user traffic — the public OSM tile server is for development and light use only
- Add at least a 500ms debounce before firing Nominatim geocoding requests in any search-as-you-type UI to stay within the ~1 request/second rate limit
- Cache tile data between app sessions where possible — flutter_map supports caching plugins (flutter_map_tile_caching) that reduce server load and speed up map rendering for repeat users
- Expose map center, zoom, and marker list as Custom Widget parameters so your FlutterFlow action flows can update the map by changing Page State variables
- Test the map widget on a real Android or iOS device from early in development — it will not render in the FlutterFlow canvas preview and you need device testing to catch layout issues
- For apps with many data points on the map, cluster markers server-side or limit the markerLatLngs list to the visible bounding box rather than rendering hundreds of markers at once
- Inform users in your app's privacy policy that address searches are processed by Nominatim (openstreetmap.org) — this is required in many jurisdictions when using third-party geocoding

## Use cases

### Property listing app with location map

A real estate app shows available rental properties pinned on an OSM map. Users can search by neighborhood name, which triggers a Nominatim geocoding call to find the lat/lng, and the Custom Widget pans the map to that location and shows property pins from a Firestore or Supabase database.

Prompt example:

```
Build a FlutterFlow screen with an OSM map widget that shows property pins from a Supabase table, with a search bar that geocodes addresses using Nominatim.
```

### Delivery tracking map for a logistics app

A last-mile delivery app for small logistics businesses shows driver locations and delivery stops on an OSM map — free of the Google Maps billing costs that grow with fleet size. The Custom Widget updates its marker list from a Realtime Database or Supabase Realtime subscription as drivers report their GPS coordinates.

Prompt example:

```
Create a FlutterFlow map screen that displays multiple moving markers from a Supabase Realtime table on an OpenStreetMap background.
```

### Community event map for a neighborhood app

A hyperlocal neighborhood app maps community events and points of interest on an OSM base layer. Residents can tap a location on the map to see event details, and organizers can add new event pins by entering an address that Nominatim converts to coordinates.

Prompt example:

```
Build an OSM map in FlutterFlow that shows event markers from a list, with a bottom sheet that appears when the user taps a marker.
```

## Troubleshooting

### The map widget shows a grey placeholder box and never renders tiles in the canvas editor

Cause: This is expected behavior for all Custom Widgets in FlutterFlow — the canvas editor cannot render platform-specific widget trees that require a real Flutter rendering context.

Solution: Click Run in FlutterFlow to switch to full test mode, or build and install the app on a real Android or iOS device. The OSM map will render correctly in either environment. You do not need to change any code — the placeholder is not an error.

### Nominatim returns HTTP 403 Forbidden for geocoding requests

Cause: Nominatim's usage policy requires all requests to include a meaningful User-Agent header identifying the application. Requests without a User-Agent, or using a generic browser user agent string, are blocked.

Solution: In your NominatimSearch API Call in FlutterFlow, open the Headers tab and add a header with Key = User-Agent and a value like MyApp/1.0 (your@email.com). Use a real app name and a contact email — Nominatim uses this to identify and contact developers of apps that violate the usage policy.

### flutter_map causes a dependency version conflict and Custom Code fails to compile

Cause: FlutterFlow bundles specific versions of Flutter and Dart, and some flutter_map versions may conflict with other packages already in the FlutterFlow dependency tree.

Solution: Try downgrading flutter_map to a previous minor version (e.g., 6.1.0 or 7.0.0) in the Custom Code dependencies panel. Remove the current version entry, add a fresh entry with the older version, and wait for FlutterFlow to sync. If conflicts persist, check the FlutterFlow community forum for the current recommended flutter_map version for your FlutterFlow version.

### Map tiles do not appear or show as broken image icons in Run mode

Cause: The OSM tile URL is being blocked due to network issues, missing User-Agent in the TileLayer configuration, or the app is running on a platform that cannot reach the OSM tile server.

Solution: Verify the userAgentPackageName in your Custom Widget matches your actual app bundle ID (e.g., com.yourcompany.yourapp). Check that the device has internet access. If testing on a web build, note that OSM tiles should work on web — but if you are behind a strict firewall, the tile.openstreetmap.org domain may be blocked. Try replacing the tile URL with a MapTiler URL as a diagnostic step to rule out the OSM server.

## Frequently asked questions

### Do I need an API key to use OpenStreetMap tiles in FlutterFlow?

No — the public OSM tile server (tile.openstreetmap.org) does not require an API key for light use. However, you must set a valid User-Agent identifying your app, and you must not use the public server for production-scale traffic. Paid tile providers like MapTiler do require an API key but are built on the same OSM data and can handle real user volumes.

### Can I use OpenStreetMap on the FlutterFlow web build?

Yes. flutter_map works on web builds in FlutterFlow. The default Flutter web renderer (html renderer) is fine for basic tile display. If you experience rendering artifacts, try the canvaskit renderer. OSM tile server supports CORS for browser requests, so there are no cross-origin issues with web builds.

### How do I show a user's live GPS location on the OSM map?

Use FlutterFlow's built-in Current Location action (under Device Utilities in the Action Flow Editor) to get the device's latitude and longitude. Store these in Page State variables and bind them to the Custom Widget's latitude and longitude parameters. The map will re-center on the current location when those values update. You can also add the device's location as an entry in the markerLatLngs list to show a 'you are here' pin.

### What is the difference between OpenStreetMap and Nominatim?

OpenStreetMap is the map data and tile rendering system — it produces the visual map images. Nominatim is a separate geocoding service that converts text addresses into latitude/longitude coordinates using OSM data. Both are operated by the OSM Foundation. In your FlutterFlow integration, the Custom Widget renders tiles from OSM, and the API Call handles geocoding via Nominatim — they serve different purposes and are called differently.

### Can I use a custom map style (different colors, dark mode) with OpenStreetMap in FlutterFlow?

The default OSM tile server serves a single standard map style. For custom styles or dark mode, use a tile provider that supports OSM data with custom styles — MapTiler and Thunderforest both offer multiple style options. Simply change the urlTemplate in your Custom Widget's TileLayer to the provider's URL (which includes an API key) to switch styles. Mapbox also supports custom styles but requires its own SDK.

---

Source: https://www.rapidevelopers.com/flutterflow-integrations/openstreetmap
© RapidDev — https://www.rapidevelopers.com/flutterflow-integrations/openstreetmap
