Skip to main content

Basic map

Contents

The Mobile Maps SDK examples require that you first complete the initial project setup.

This example provides a simple reference of how to display and render the map in a Jetpack Compose environment. It will display a basic map, similar to the Basic Map code example for Java and Kotlin.

Basic map

import android.content.Context
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.viewinterop.AndroidView
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleEventObserver
import androidx.lifecycle.compose.LocalLifecycleOwner
import com.trimblemaps.account.LicensedFeature
import com.trimblemaps.account.TrimbleMapsAccountManager
import com.trimblemaps.account.models.TrimbleMapsAccount
import com.trimblemaps.mapsdk.TrimbleMaps
import com.trimblemaps.mapsdk.maps.MapView
import com.trimblemaps.mapsdk.maps.OnMapReadyCallback
import com.trimblemaps.mapsdk.maps.Style

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            // IMPORTANT: Use your real API key
            TrimbleMapScreen(apiKey = "YOUR-API-KEY")
        }
    }
}

// Your composables

/**
 * This composable uses the AndroidView to display the map, it also handles the authentication
 * before displaying the map.
 */
@Composable
fun TrimbleMapScreen(
    apiKey : String,
    mapStyle : String = Style.MOBILE_DAY
) {
    val context = LocalContext.current
    val lifecycleOwner = LocalLifecycleOwner.current

    // Gate rendering the MapView until account initialization completes
    var isInitialized by remember { mutableStateOf(false) }

    // Initialize the Trimble account/session once.
    LaunchedEffect(apiKey) {
        // Build account with your API key and the Maps SDK licensed feature
        val account = TrimbleMapsAccount.builder()
            .apiKey(apiKey)
            .addLicensedFeature(LicensedFeature.MAPS_SDK)
            .build()

        // Initialize and block until ready (mirrors awaitInitialization in sample)
        TrimbleMapsAccountManager.initialize(account)
        TrimbleMapsAccountManager.awaitInitialization()

        // Obtain the Trimble Maps instance BEFORE creating/using MapView
        TrimbleMaps.getInstance(context)

        isInitialized = true
    }

    if(isInitialized) {
        // Create a single MapView instance tied to this composition
        val mapView = rememberMapViewWithLifecycle(context, lifecycleOwner.lifecycle)

        AndroidView(
            modifier = Modifier.fillMaxSize(),
            factory = { mapView },
            update = {view ->
                // Request a TrimbleMapsMap asynchronously (like getMapAsync in the sample)
                view.getMapAsync(OnMapReadyCallback { trimbleMap ->
                    // Apply a style to render the map; OnStyleLoaded callback optional
                    trimbleMap.setStyle(mapStyle) {
                        // Style is loaded; add data, move camera, etc. here if needed
                    }
                })
            }
        )
    }



}

/**
 * Handle the lifecycle events from the map view, onDestroy etc.
 */
@Composable
private fun rememberMapViewWithLifecycle(
    context: Context,
    lifecycle: Lifecycle
): MapView {
    // Create once per composition
    val mapView = remember {
        MapView(context).apply {
            // Any MapView-level config before onMapReady can live here
            // (Gestures, UI settings, etc., once you have references)
        }
    }

    // Hook the MapView into the lifecycle
    DisposableEffect(lifecycle, mapView) {
        val observer = LifecycleEventObserver { _, event ->
            when (event) {
                Lifecycle.Event.ON_CREATE -> { /* no-op (Trimble handled) */ }
                Lifecycle.Event.ON_START  -> mapView.onStart()
                Lifecycle.Event.ON_RESUME -> mapView.onResume()
                Lifecycle.Event.ON_PAUSE  -> mapView.onPause()
                Lifecycle.Event.ON_STOP   -> mapView.onStop()
                Lifecycle.Event.ON_DESTROY -> mapView.onDestroy()
                else -> Unit
            }
        }
        lifecycle.addObserver(observer)

        // If your Activity receives onLowMemory, you can call mapView.onLowMemory() there.
        onDispose {
            lifecycle.removeObserver(observer)
            // onDestroy() is forwarded above, but if the lifecycle ends abruptly,
            // this ensures cleanup.
            try { mapView.onDestroy() } catch (_: Throwable) {}
        }
    }

    return mapView
}
Last updated October 14, 2025.
Contents