Mini-Map: Embedding Floor Plan Previews in Your App
A mini-map is a floor plan preview embedded directly in your content, for example on an exhibitor detail screen. Tapping it expands to a full interactive map where users can explore the floor plan, zoom, and get wayfinding directions to a booth.
Full working examples are available on GitHub:
The Pattern
Both example apps follow a three-screen flow:
- Exhibitors List — loads exhibitors from the SDK and renders them as a scrollable list.
- Exhibitor Detail — shows a mini-map preview of the selected booth with Expand and Directions buttons.
- Fullscreen Plan — the mini-map expands into a full interactive plan with Collapse and Directions controls.
The core SDK APIs shared across platforms:
| API | Purpose |
|---|---|
setElementsVisibility | Hide default SDK chrome for a clean embedded look |
selectExhibitor | Highlight a booth by name or ID |
selectRoute | Draw a wayfinding path between two booths |
fitBounds | Reset zoom to show the full plan |
iOS (SwiftUI)
Demo
Quick Start
git clone https://github.com/expofp/expofp-sdk-ios-examples.git
cd expofp-sdk-ios-examples/MiniMap
open MiniMap.xcodeprojBuild and run on a simulator or device (Xcode 16+, iOS 17+).
Architecture
- Two
PlanPresenterinstances —miniMapfor the inline preview,mapfor the fullscreen view. Both share the same expo key but operate independently. matchedGeometryEffect— SwiftUI animates the transition between inline and fullscreen by matching geometry via a sharedNamespace.allowsHitTesting(false)— the inline preview ignores all touch input so it acts as a static thumbnail.
Key Code
Two-presenter pattern (ContentStore.swift):
@MainActor
final class ContentStore: ObservableObject {
let miniMap = ExpoFpPlan.createPlanPresenter(with: .expoKey("demo"))
let map = ExpoFpPlan.createPlanPresenter(with: .expoKey("demo"))
}Plan ready → hide chrome → load exhibitors (ContentView.swift excerpt):
.onReceive(store.miniMap.planStatusPublisher) { status in
self.status = status
if status == .ready && exhibitors.isEmpty {
let visibility = ExpoFpElementsVisibility(
controls: false, levels: false, header: false, overlay: false
)
store.miniMap.setElementsVisibility(visibility)
store.map.setElementsVisibility(visibility)
Task {
exhibitors = try await store.miniMap.exhibitorsList().get()
}
}
}Expand/collapse with booth selection and directions (ExhibitorView.swift excerpt):
store.miniMap.getView()
.frame(height: 200)
.allowsHitTesting(false)
.matchedGeometryEffect(id: exhibitor.id, in: namespace, isSource: !showPlan)
// On appear — highlight booth on both presenters
.onAppear {
store.miniMap.selectExhibitor(nameOrExternalId: exhibitor.externalId)
store.map.selectExhibitor(nameOrExternalId: exhibitor.externalId)
}
// Directions button
Button {
withAnimation(.easeInOut) {
showPlan = true
store.map.selectRoute(from: .booth("Entrance"), to: .booth("4.1-36"))
}
} label: {
HStack(spacing: 4) {
Image(.directions)
Text("Directions").foregroundStyle(.black)
}
}SDK APIs Used
| API | Purpose |
|---|---|
createPlanPresenter | Create a plan presenter instance bound to an expo key |
selectExhibitor | Highlight a booth by name or external ID (no args to deselect) |
fitBounds | Reset the map zoom to show all content |
selectRoute | Draw a wayfinding path between two booths |
setElementsVisibility | Hide/show default SDK UI controls |
exhibitorsList | Fetch the list of exhibitors for the plan |
planStatusPublisher | Combine publisher that emits plan loading status changes |
getView | Get the SwiftUI view for the plan presenter |
Android (Jetpack Compose)
Demo

Quick Start
git clone https://github.com/expofp/expofp-sdk-android-examples.git
cd expofp-sdk-android-examplesOpen in Android Studio, select the mini-map run configuration, and run on a device or emulator (min SDK 26).
Architecture
- Two
PlanPresenterinstances —PlanManagercreatesminiMapPresenterfor the inline preview andfullMapPresenterfor the fullscreen view, both sharing the same expo key. - MVVM + Hilt DI —
PlanManageris a@Singletoninjected into ViewModels. - Pre-attached WebViews — both presenters' views are added to the activity's content container with
alpha = 0finMainActivity.onCreate(), ensuring the underlying WebViews initialize early and avoiding blank-frame issues when they appear in Compose later. - Fullscreen overlay — the mini-map lives in the scrollable detail content; the fullscreen map is a separate
AnimatedVisibilityoverlay withfadeIn/fadeOut. No WebView detach/reattach needed. - View reparenting — the SDK returns the same native
Viewfor a given presenter, soPlanMapViewdetaches it from any previous parent before re-adding.
Key Code
Two-presenter pattern (PlanManager.kt excerpt):
@Singleton
class PlanManager @Inject constructor() {
var miniMapPresenter: IExpoFpPlanPresenter? = null
private set
var fullMapPresenter: IExpoFpPlanPresenter? = null
private set
fun createPresenters() {
if (miniMapPresenter != null) return
val params = listOf(
ExpoFpPlanParameter.NoOverlay(true),
ExpoFpPlanParameter.HideHeaderLogo(true)
)
miniMapPresenter = ExpoFpPlan.createPlanPresenter(
planLink = ExpoFpLinkType.ExpoKey(PLAN_KEY),
additionalParams = params
)
fullMapPresenter = ExpoFpPlan.createPlanPresenter(
planLink = ExpoFpLinkType.ExpoKey(PLAN_KEY),
additionalParams = params
)
}
}AndroidView bridge with view reparenting (PlanMapView.kt):
@Composable
fun PlanMapView(
presenter: IExpoFpPlanPresenter,
modifier: Modifier = Modifier
) {
AndroidView(
factory = { context ->
val planView = presenter.getView()
(planView.parent as? ViewGroup)?.removeView(planView)
planView.alpha = 1f
FrameLayout(context).apply {
layoutParams = FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.MATCH_PARENT
)
addView(planView)
}
},
onRelease = { it.removeAllViews() },
modifier = modifier
)
}Exhibitor selection, directions, and expand (ExhibitorDetailViewModel.kt excerpt):
fun onScreenEnter() {
forBothPresenters { presenter ->
presenter.selectExhibitor(exhibitorName)
presenter.fitBounds()
}
}
fun showDirections() {
val boothName = _uiState.value.boothName ?: return
val presenter = planManager.fullMapPresenter ?: return
presenter.selectRoute(
from = ExpoFpRouteWaypoint.Booth(PlanManager.ENTRANCE_BOOTH),
to = ExpoFpRouteWaypoint.Booth(boothName)
)
}
fun expandWithDirections() {
_uiState.update { it.copy(isMapExpanded = true) }
showDirections()
}SDK APIs Used
| API | Purpose |
|---|---|
ExpoFpPlan.initialize | Initialize the SDK (call once in Application) |
createPlanPresenter | Create a plan presenter bound to an expo key |
planStatusFlow | StateFlow that emits plan loading status changes |
getView | Get the native Android View for the plan |
exhibitorsList | Fetch the list of exhibitors |
boothsList | Fetch the list of booths |
selectExhibitor | Highlight a booth by exhibitor name |
selectRoute | Draw a wayfinding path between two booths |
setElementsVisibility | Hide/show default SDK UI controls |
fitBounds | Reset the map zoom to show all content |
Next Steps
- Browse the full source: iOS example · Android example
- iOS Swift SDK v5 docs
- Android Kotlin SDK v5 docs
- JavaScript API Reference