Google Map JS interop Composable integration - pos...
# compose-web
r
Google Map JS interop Composable integration - posting here for anyone in the future who might be trying the same Functionalities: basic + set center + click to add a marker (In thread)
🙏 3
1. in your google cloud project, create a static map API key and create a new map ID (it is needed for displaying markers) 2. define the script in your html
<script async defer src="<https://maps.googleapis.com/maps/api/js?key=API_KEY&libraries=marker>"></script>
3. define bindings
Copy code
import org.w3c.dom.Element

@JsName("google.maps.Map")
external class GMap(element: Element, options: JsAny) {
    @JsName("addListener")
    fun addListener(eventName: String, handler: (MapsMouseEvent) -> Unit): JsAny
}

@JsName("google.maps.marker.AdvancedMarkerElement")
external class GAdvancedMarkerElement(options: JsAny) {
    fun setPosition(position: JsAny)
    fun setMap(map: GMap?)
}

external interface LatLng {
    fun lat(): Double
    fun lng(): Double
}

external interface MapsMouseEvent {
    val latLng: LatLng
}

fun createMarkerOptions(lat: Double, lng: Double): JsAny = js("""
            ({
                position: { lat: lat, lng: lng }
            })
        """)

fun createGMapOptions(lat: Double, lng: Double, zoomLevel: Double, mapType: String): JsAny = js("""
    ({
        center: { lat: lat, lng: lng },
        zoom: zoomLevel,
        mapTypeId: mapType,
        mapId: "MAP_ID_HERE"
    })
""")
4. define the composable and connect the map element
Copy code
@Composable
fun GoogleMapView(
    modifier: Modifier = Modifier,
    lat: Double,
    lng: Double,
    markerPos: MapPos?,
    zoomLevel: Double = 8.0,
    mapType: String = "roadmap", // "satellite", "hybrid", "terrain"
    onClick: (ClickedLatLng) -> Unit,
) {
    val mapRef = remember { mutableStateOf<GMap?>(null) }
    val markers = remember {
        mutableListOf<GAdvancedMarkerElement>()
    }

    HtmlView(
        modifier = modifier,
        factory = {
            createElement("div").apply { id = "map" }
        },
        update = { element ->
            if (mapRef.value == null) {
                mapRef.value = GMap(element, createGMapOptions(lat, lng, zoomLevel, mapType)).apply {
                    addListener("click", handler = { event ->
                        onClick(ClickedLatLng(lat = event.latLng.lat(), long = event.latLng.lng()))
                    })
                }
            } else {
                if (markerPos != null) {
                    for (marker in markers) {
                        marker.setMap(null)
                    }
                    markers.clear()
                    val map = mapRef.value!!
                    val newMarker = GAdvancedMarkerElement(createMarkerOptions(markerPos.lat, markerPos.long)).apply {
                        setMap(map)
                    }
                    markers.add(newMarker)
                }
            }
        },
    )
}
(implementation of HtmlView)