Rok Oblak
09/09/2025, 11:59 AMHTMLView fom here to WebElementView with 1.9.0-rc-01.
However, now my markers aren't showing on the correct position when I click on a map. It is "kind of close", but not quite. When panning the map, the marker is pinned to the same geolocation, but when zooming, it moves up and down.
(Code in thread - so even when markerPos does not change, it appears to float up and down as I zoom in and out)
Any ideas why?Rok Oblak
09/09/2025, 12:00 PMRok Oblak
09/09/2025, 12:00 PMpackage googlemaps
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.viewinterop.WebElementView
import kotlinx.browser.document
import org.w3c.dom.HTMLElement
import ui.common.maps.ClickedLatLng
import ui.common.maps.MapPos
import ui.common.maps.Waypoints
@Composable
fun WebGoogleMapView(
lat: Double,
lng: Double,
markerPos: MapPos?,
waypoints: Waypoints?,
zoomLevel: Double = 10.0,
interactionEnabled: Boolean, // TODO: P2: handle interactionEnabled
mapType: String = "roadmap", // "satellite", "hybrid", "terrain"
onClick: (ClickedLatLng) -> Unit,
modifier: Modifier = Modifier,
) {
val mapRef = remember { mutableStateOf<GMap?>(null) }
val markers = remember {
mutableListOf<GAdvancedMarkerElement>()
}
val waypointMarkers = remember { mutableListOf<GAdvancedMarkerElement>() }
val polylineRef = remember { mutableStateOf<GPolyline?>(null) }
LaunchedEffect(lat, lng) {
mapRef.value?.setCenter(GLatLng(lat, lng))
}
WebElementView(
modifier = modifier,
factory = {
(document.createElement("div")
as HTMLElement)
.apply { id = "map" }
},
update = { element ->
val map = mapRef.value
if (map == 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 {
for (marker in markers) {
marker.setMap(null)
}
markers.clear()
if (markerPos != null) {
val newMarker = GAdvancedMarkerElement(
createMarkerOptions(markerPos.lat, markerPos.long)
).apply {
setMap(map)
}
markers.add(newMarker)
}
waypoints?.start?.let {
val pos = it.pos
val text = it.text
val textMarker =
GAdvancedMarkerElement(createTextMarkerOptions(pos.lat, pos.long, text))
textMarker.setMap(map)
}
waypoints?.end?.let {
val pos = it.pos
val text = it.text
val textMarker =
GAdvancedMarkerElement(createTextMarkerOptions(pos.lat, pos.long, text))
textMarker.setMap(map)
}
for (wm in waypointMarkers) {
wm.setMap(null)
}
waypointMarkers.clear()
val positions = waypoints?.positions.orEmpty()
if (positions.isNotEmpty()) {
val adaptedPositions = listOfNotNull(
positions.firstOrNull(),
positions.lastOrNull()
).distinct()
adaptedPositions.forEach { pos ->
val wMarker = GAdvancedMarkerElement(
createMarkerOptions(pos.lat, pos.long)
).apply {
setMap(map)
}
waypointMarkers.add(wMarker)
}
}
val path = positions.map { latLngLiteral(it.lat, it.long) }.toTypedArray()
val existingPolyline = polylineRef.value
if (existingPolyline == null) {
polylineRef.value =
GPolyline(createPolylineOptions(path.toList().toJsArray())).apply {
setMap(map)
}
} else {
// Just update the path
existingPolyline.setPath(path.toJsArray())
}
}
},
)
}
Utils:
package googlemaps
fun latLngLiteral(lat: Double, lng: Double): JsAny =
js("({ lat: lat, lng: lng })")
fun createMarkerOptions(lat: Double, lng: Double): JsAny = js("""
({
position: { lat: lat, lng: lng }
})
""")
fun createPolylineOptions(path: JsArray<JsAny>): JsAny = js("""
({
path: path,
geodesic: true,
strokeColor: '#FF0000',
strokeOpacity: 1.0,
strokeWeight: 8
})
""")
fun createGMapOptions(lat: Double, lng: Double, zoomLevel: Double, mapType: String): JsAny = js("""
({
center: { lat: lat, lng: lng },
zoom: zoomLevel,
mapTypeId: mapType,
mapId: "9d6d8ac542bcf9c1"
})
""")
fun createTextMarkerOptions(lat: Double, lng: Double, text: String): JsAny = js("""
(function() {
var div = document.createElement('div');
div.style.transform = 'translate(-50%, -50%)';
div.style.padding = '8px';
div.style.backgroundColor = 'white';
div.style.border = '1px solid black';
div.style.whiteSpace = 'nowrap';
div.innerText = text;
return {
position: { lat: lat, lng: lng },
content: div
};
})()
""")