Search#
Preview - requires Android 3.0.0-beta01, or iOS 3.3.0-beta.2 or higher
Add a full drop-in and customisable place search experience, in only a few lines of code. The component also allows choosing the start and end from a map, and optionally showing inline Route results.
Integration#
See the code examples below, or for examples in the sample apps, see
Quick start#
// Or BottomSheetSearchWithMapViewController for the UIKit wrapper
BottomSheetSearchWithMapView(
// A fallback location for the map center e.g. if no location permission
// is granted
defaultMapFocus: .center(defaultPlace),
searchProviderFactory: appleSearchProviderFactory(
// Bias the search results to a known relevant region
region: MKCoordinateRegion(center: bigBen,
span: LocationConstants.defaultSpan)),
dismissAction: {
// Back button action
},
searchDidComplete: { routePlanningSpec in
// Do something with the resulting start/end
}
)
BottomSheetSearchWithMapLayout(
// A fallback location for the map center e.g. if no location permission
// is granted
defaultMapFocus = MapFocus.onPoint(defaultPlace),
searchProviderFactory = googleSearchProviderFactory(
googlePlacesApiKey = "...",
// Bias the search results to a known relevant region
region = regionBounds
),
onSearchCompleted = { routePlanningSpec ->
// Do something with the resulting start/end
}
)
<com.citymapper.sdk.ui.search.view.BottomSheetSearchWithMapView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/search_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
fun onViewCreated(view: View, savedInstanceState: Bundle?) {
val searchView =
view.requireViewById<BottomSheetSearchWithMapView>(R.id.search_view)
searchView.configure(
// A fallback location for the map center e.g. if no location permission
// is granted
defaultMapFocus = MapFocus.onPoint(defaultPlace),
searchProviderFactory = googleSearchProviderFactory(
googlePlacesApiKey = "...",
// Bias the search results to a known relevant region
region = regionBounds
),
onSearchCompleted = { routePlanningSpec ->
// Do something with the resulting start/end
}
)
}
Inline results#
The layout can be customised to show a top app bar and inline route results, instead of needing to navigate away to plan routes.
BottomSheetSearchWithMapView(
// A fallback location for the map center e.g. if no location permission
// is granted
defaultMapFocus: .center(defaultPlace),
searchProviderFactory: appleSearchProviderFactory(
region: MKCoordinateRegion(center: bigBen,
span: LocationConstants.defaultSpan)),
dismissAction: {
// Back button action
},
searchCompleteView: { scope in
scope.routeResults(
planBuilder: { builder in
builder.walkRoute()
builder.scooterRoute()
builder.transitRoutes()
},
didTapRoute: { route in
if DirectionsViewController.supportsRoute(route: route) {
// Open route details using DirectionsViewController
} else {
// Open route details using RouteDetailsContainer.createWrappedRouteDetailsController
}
}
)
}
)
BottomSheetSearchWithMapLayout(
// A fallback location for the map center e.g. if no location permission
// is granted
defaultMapFocus = MapFocus.onPoint(defaultPlace),
searchProviderFactory = googleSearchProviderFactory(
googlePlacesApiKey = "...",
// Bias the search results to a known relevant region
region = regionBounds
),
topAppBarContent = {
DefaultTopAppBar(onNavigateUp = { finish() })
},
searchCompleteContent = {
// This is the content shown when the search completes
// IMPORTANT - the gradle module must have the Jetpack compose compiler enabled,
// as detailed at https://developer.android.com/jetpack/compose/setup#setup-compose
// An example of customising this with your own state
val myExampleState by stateStoreStateFlow.collectAsState()
RouteResults(planBuilder = {
// Customise the routes planned. This lambda is aware of compose state,
// so any state changes read here will cause the routes to update
if (myExampleState) {
walkRoute()
bikeRoute()
transitRoutes()
} else {
// Show some different combination of routes
}
}, onRouteClick = { route ->
if (CitymapperDirectionsView.supportsRoute(route)) {
// Show route detail using CitymapperDirectionsView
} else {
// Show route detail using RouteDetail.showStandaloneRouteDetailScreen
}
})
}
)
// IMPORTANT! The `topAppBarContent` and `sheetContent` parameters take
// Composables, not views. For that reason, to customise the search layout,
// the gradle module must have the Jetpack compose compiler enabled,
// as detailed at https://developer.android.com/jetpack/compose/setup#setup-compose
searchView.configure(
// A fallback location for the map center e.g. if no location permission
// is granted
defaultMapFocus = MapFocus.onPoint(defaultPlace),
searchProviderFactory = googleSearchProviderFactory(
googlePlacesApiKey = "...",
// Bias the search results to a known relevant region
region = regionBounds
),
topAppBarContent = {
DefaultTopAppBar(onNavigateUp = { finish() })
},
searchCompleteContent = {
// This is the content shown when the search completes
// IMPORTANT - the gradle module must have the Jetpack compose compiler enabled,
// as detailed at https://developer.android.com/jetpack/compose/setup#setup-compose
// An example of customising this with your own state
val myExampleState by stateStoreStateFlow.collectAsState()
RouteResults(planBuilder = {
// Customise the routes planned. This lambda is aware of compose state,
// so any state changes read here will cause the routes to update
if (myExampleState) {
walkRoute()
bikeRoute()
transitRoutes()
} else {
// Show some different combination of routes
}
}, onRouteClick = {
if (CitymapperDirectionsView.supportsRoute(route)) {
// Show route detail using CitymapperDirectionsView
} else {
// Show route detail using RouteDetail.showStandaloneRouteDetailScreen
}
})
}
)