Quick start¶
1. Set the API key¶
The key is set once, before anything else touches MapKit. Get it from the official documentation.
fun initMapKit() {
MapKit.setApiKey("<API_KEY>")
}
Keeping the key out of the sources
BuildKonfig generates it into common code at build time; this is what the sample does.
Call it from the entry point of each platform.
class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
initMapKit()
}
}
@main
struct iOSApp: App {
init() {
AppKt.doInitMapKit()
}
var body: some Scene {
WindowGroup { ContentView() }
}
}
Any other entry point works too — see the official Android and iOS guides.
2. Initialize MapKit¶
On Android MapKit needs a Context and has to be told when the application comes to the foreground;
on iOS neither is required. There are three ways to arrange that, and they can be mixed as long as
nothing is done twice.
From Android sources¶
MapKit.initialize(Context) exists in androidMain only. The lifecycle calls go with it.
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
MapKit.initialize(this)
}
override fun onStart() {
super.onStart()
MapKit.getInstance().onStart()
}
override fun onStop() {
super.onStop()
MapKit.getInstance().onStop()
}
}
From common code, with Compose¶
Requires yandex-mapkit-kmp-compose
rememberAndInitializeMapKit() does the Android-only initialization and returns the instance;
bindToLifecycleOwner() calls onStart() and onStop() for you, and onStop() again when the
composable leaves the composition.
@Composable
fun MapScreen() {
rememberAndInitializeMapKit().bindToLifecycleOwner()
/* ... */
}
Initialize once
rememberAndInitializeMapKit() initializes MapKit on the first composition. If something
already did that — MapKit.initialize(Context) in an Activity, for example — use
rememberMapKit() instead, which only returns the instance.
Note also that disposing the composition that owns bindToLifecycleOwner() stops MapKit.
Around a view you already have¶
Every wrapped native type has a toCommon() extension in the platform source set, so a MapView
created the way the official SDK does it can be handed to common code:
MapView.toCommon() / YMKMapView.toCommon(), MapWindow.toCommon() / YMKMapWindow.toCommon(),
Map.toCommon() / YMKMap.toCommon(). The opposite direction is toNative(), also platform-only.
This is the path described in Wrapper overview.
3. Show a map¶
private val moscow = CameraPosition(
target = Point(55.751225, 37.629540),
zoom = 15f,
azimuth = 0f,
tilt = 0f,
)
@Composable
fun MapScreen() {
rememberAndInitializeMapKit().bindToLifecycleOwner()
val cameraPositionState = rememberCameraPositionState { position = moscow }
YandexMap(
cameraPositionState = cameraPositionState,
modifier = Modifier.fillMaxSize(),
)
}
In commonMain:
fun setupMap(map: Map) {
map.move(
CameraPosition(Point(55.751225, 37.629540), zoom = 15f, azimuth = 0f, tilt = 0f),
)
}
In androidMain:
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
MapKit.initialize(this)
setContentView(R.layout.activity_main)
val mapView = findViewById<com.yandex.mapkit.mapview.MapView>(R.id.map)
setupMap(mapView.mapWindow.map.toCommon())
}
}
4. Add a placemark¶
@Composable
fun MapScreen() {
rememberAndInitializeMapKit().bindToLifecycleOwner()
val cameraPositionState = rememberCameraPositionState { position = moscow }
YandexMap(
cameraPositionState = cameraPositionState,
modifier = Modifier.fillMaxSize(),
) {
Placemark(
state = rememberPlacemarkState(Point(55.751225, 37.629540)),
icon = imageProvider(Res.drawable.pin_red),
onTap = { point ->
println("tapped at $point")
true
},
)
}
}
fun setupMap(map: Map, icon: ImageProvider) {
map.mapObjects.addPlacemark().apply {
geometry = Point(55.751225, 37.629540)
setIcon(icon)
}
}
ImageProvider is built in platform code — see Image resources.
5. React to events¶
Listeners are not retained by MapKit, so every subscription in this wrapper takes a
WeakRef<Listener> and the caller keeps the strong reference.
YandexMap(modifier = Modifier.fillMaxSize()) {
MapListeners(
onMapTap = { point -> println("tap at $point") },
onMapLoaded = { statistics -> println("loaded in ${statistics.fullyLoaded}") },
)
}
class MapController(private val map: Map) {
private val inputListener = InputListener(
onMapTap = { _, point -> println("tap at $point") },
onMapLongTap = { _, point -> println("long tap at $point") },
)
init {
map.addInputListener(inputListener.asWeakRef())
}
}
Storing inputListener in a field is part of the pattern: a listener referenced only by the
subscription is collected, and the subscription silently stops firing. See
Wrapper overview.
Where to go next¶
- Migration from MapKit — porting existing Android map code.
- Map and camera — the camera, gestures, styles, screen coordinates.
- Map objects — placemarks, geometry, clustering.
- Compose overview — the composable API in full.