Docs / Map view / Switching providers

Switching providers

Exactly three things differ per provider: the dependency, the map view type and the state object type. Markers, shapes, camera work and events are written the same way regardless. You can even keep several providers in one screen and switch between them at runtime.

ANDROID
8 providers
iOS
9 providers
REACT
13 providers

01 · What changes, what does not

CHANGES
  • The dependency (for-maplibre → for-googlemaps)
  • The map view type (MapLibreMapView → GoogleMapView)
  • The state object type and its factory
  • The design constant handed to mapDesignType
  • How the API key is obtained and the SDK initialised
STAYS THE SAME
  • GeoPoint · GeoRectBounds · MapCameraPosition
  • MarkerState · PolylineState · PolygonState · CircleState …
  • moveCameraTo() · fitBounds() · event handlers
  • Geodesy (distance, heading, area)
  • Extension layers (clustering, heatmap, GeoJSON)

The state types are per provider, but they all implement one shared interface — MapViewStateInterface on Android and React, MapViewStateProtocol on iOS. Camera work and design switching can be written against that interface.

Figure · what each provider actually draws, from the sample apps
GOOGLE MAPS
MAPLIBRE
MAPBOX
HERE
ARCGIS

02 · Types per provider

What each provider package actually exports. For historical reasons the names are not perfectly symmetric, so this table lists the exported symbols verbatim.

Provider
Android
iOS
React
Google Maps
GoogleMapView
rememberGoogleMapViewState
GoogleMapViewState
GoogleMapView
GoogleMapViewState
GoogleMapView2D / GoogleMapView
useGoogleMapViewState
MapLibre
MapLibreMapView
rememberMapLibreMapViewState
MapLibreViewState
MapLibreMapView
MapLibreViewState
MapLibreMapView2D
useMapLibreViewState
Mapbox
MapboxMapView
rememberMapboxMapViewState
MapboxViewState
MapboxMapView
MapboxViewState
MapBoxMapView2D / MapBoxMapView
useMapboxViewState
HERE
HereMapView
rememberHereMapViewState
HereViewState
HereMapView
HereMapViewState
HereMapView2D
useHereViewState
ArcGIS
ArcGISMapView / 2D
rememberArcGISMapViewState
ArcGISMapViewState
ArcGISMapView / 2D
ArcGISMapViewState
ArcGISMapView2D / ArcGISMapView
useArcGISViewState
MapTiler
MapTilerMapView
rememberMapTilerMapViewState
MapTilerViewState
MapTilerMapView
MapTilerViewState
MapTilerMapView2D
useMapTilerViewState
TomTom
TomTomMapView
rememberTomTomMapViewState
TomTomMapViewState
TomTomMapView
TomTomMapViewState
TomTomMapView2D
useTomTomViewState
Longdo
LongdoMapView
rememberLongdoMapViewState
LongdoViewState
LongdoMapView
LongdoViewState
LongdoMapView2D
useLongdoViewState
Apple MapKit
MapKitMapView
MapKitViewState
MapKitMapView
useMapKitViewState
Leaflet
LeafletMapView
useLeafletMapViewState
OpenLayers
OpenLayersMapView
useOpenLayersMapViewState
Azure Maps
AzureMapsMapView
useAzureMapsViewState
Cesium
CesiumMapView
useCesiumMapViewState

“/ 2D” means the package exposes both a 3D and a 2D view. They share one state object; in 2D, tilt is always 0.

Platform

03 · Writing the switch

To switch at runtime, hold one state object per provider and branch only on which view to render. The overlay declarations stay outside the branch. This is exactly the shape the sample apps use.

MapViewContainer.kt · Jetpack Compose
@Composable
fun MapViewContainer(
    modifier: Modifier = Modifier,
    state: MapViewStateInterface<*>? = null,
    content: (@Composable MapViewScope.() -> Unit)? = null,
) {
    when (state) {
        is GoogleMapViewState -> GoogleMapView(modifier, state, content = content)
        is MapLibreViewState -> MapLibreMapView(modifier, state, content = content)
        is MapboxViewState -> MapboxMapView(modifier, state, content = content)
        is HereViewState -> HereMapView(modifier, state, content = content)
        else -> { /* not selected */ }
    }
}

// 呼び出し側はオーバーレイだけを書く
MapViewContainer(state = selectedState) {
    Markers(markerStates)
}

React does not allow the hook call order to change, so the safe shape is one small component per provider that calls its own hook. The sample app providers/ directory is laid out that way.

What the video shows · Android + MapLibreKotlin · Jetpack Compose
// One state per provider
val maplibre = rememberMapLibreMapViewState(
    mapDesign = MapLibreDesign.OsmBrightJa,
    cameraPosition = MapCameraPosition(
        position = GeoPoint.fromLatLong(35.6812, 139.7671),
        zoom = 12.0,
    ),
)
val googlemaps = rememberGoogleMapViewState(
    cameraPosition = MapCameraPosition(
        position = GeoPoint.fromLatLong(35.6812, 139.7671),
        zoom = 12.0,
    ),
)
var useMapLibre by remember { mutableStateOf(true) }

// The overlays are declared outside the branch, so swapping does not touch them
val overlays: @Composable MapViewScope.() -> Unit = {
    Marker(markerState)
    Polyline(routeState)
}

// Hand the camera you were just looking at to the side you are switching to
LaunchedEffect(useMapLibre) {
    if (useMapLibre) {
        maplibre.moveCameraTo(googlemaps.cameraPosition)
    } else {
        googlemaps.moveCameraTo(maplibre.cameraPosition)
    }
}

if (useMapLibre) {
    MapLibreMapView(state = maplibre, content = overlays)
} else {
    GoogleMapView(state = googlemaps, content = overlays)
}
Sample video · switching providers at runtime
Video not shot yetThe sample app swapping only the map view while the overlays stay declared. The point lands when the markers and polylines are visibly in the same places before and after the swap.

04 · Handling provider differences

Not every map SDK can do everything. MapConductor closes the gap where it can, and substitutes where it cannot.

1 · SUBSTITUTE
Where the native SDK lacks a feature, the SDK draws it as raster tiles instead — polygon holes and very large marker sets take this path. Your call sites do not change.
2 · TYPED
An element a provider cannot handle simply has no renderer in that driver. Whether it is available is visible at compile time, or from the state object type.
3 · ESCAPE HATCH
If you genuinely need something provider-specific, pull the native map instance out of MapViewHolder and call it directly.
API KEYS

Keys and initialisation stay per provider

MapConductor never holds your API key. Each map SDK is fed the way it expects — the Android manifest, the iOS sdkInitialize closure, a config object on the web. MapLibre, Leaflet and OpenLayers need no key at all; there you only choose where the tiles come from.

Related pages