fix(map): replace MarkerComposable with Canvas-rendered bitmaps#5702
Merged
jamesarich merged 3 commits intoJun 1, 2026
Conversation
Eliminates the FATAL crash "The ComposeView was measured to have a width or height of zero" (Crashlytics issue 66ee057e) caused by maps-compose's renderComposableToBitmapDescriptor creating off-screen ComposeViews that can fail to measure during subcomposition races. Instead of relying on the library's fragile synchronous compose→measure pipeline, render marker bitmaps directly using Canvas: - NodeChip markers: colored rounded rect + centered text - Waypoint emoji markers: text painted at 32sp This removes all usages of MarkerComposable and rememberComposeBitmapDescriptor across InlineMap, MapView (NodeTrack and Traceroute overlays), and WaypointMarkers. Fixes: googlemaps/android-maps-compose#875 (zero-size variant) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Self-review fixes to the Canvas-rendered marker bitmaps: - Render strikethrough text when node.isIgnored (matching NodeChip) - Include isIgnored in remember keys so bitmap recomputes on toggle - Use scaledDensity (density * fontScale) for text sizing to respect accessibility font scale settings Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This was referenced Jun 1, 2026
jamesarich
added a commit
that referenced
this pull request
Jun 2, 2026
…p, drop Google Maps leftovers Rebased onto main and dusted off the MapLibre Compose Multiplatform branch. - Bump maplibre-compose 0.12.1 -> 0.13.0 and migrate the redesigned location API (BearingUpdate.TRACK_LOCATION -> TRACK_AUTOMATIC; LocationPuck locationState -> location). - desktopApp: add a host-detecting maplibre-native-bindings-jni runtime backend so the desktop base map renders (Metal on macOS arm64, OpenGL on Linux/Windows amd64). Without it Gradle links no native renderer and the map canvas is black. - Gate Compose map overlays off on the JVM target via a mapOverlaysSupported expect/actual flag (true Android/iOS, false desktop). maplibre-compose 0.13.0 stubs the desktop layers/sources API with TODO(), so composing markers/waypoints/tracks/traceroute threw NotImplementedError and tore down the window. Desktop now renders base-map-only; overlays auto-enable when upstream implements desktop layers. - Remove orphaned Google Maps leftovers the rebase carried over from main's #5702/#5709 marker work (MarkerBitmapRenderer.kt + play-services-maps); this branch replaces Google Maps entirely. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
The maps-compose library's
MarkerComposableandrememberComposeBitmapDescriptorcreate an off-screenComposeView, measure it synchronously, and throw if width/height is zero. This causes a recurring crash (renderComposableToBitmapDescriptor-- "measured to have a width or height of zero") that has been hitting production since 2.7.3 and is still present in build 29321001 (Crashlytics issue66ee057e).The root cause is a subcomposition race: when the Recomposer is paused or the parent hasn't laid out yet, the synchronous measure returns zero and the library throws unconditionally. Unlike the
Clusteringcomposable (which has internalfakeLifecycleOwnerprotection), standaloneMarkerComposablehas no safety net.Approach
Replace all standalone
MarkerComposable/rememberComposeBitmapDescriptorusages across the app with Canvas-rendered bitmap descriptors that never go through a ComposeView:MarkerBitmapRenderer.ktprovidesrememberNodeChipDescriptor()andrememberEmojiMarkerDescriptor()-- pure Canvas/TextPaint rendering with properremembercaching.MarkerComposable { NodeChip(...) }withMarker(icon = rememberNodeChipDescriptor(node)).NodeTrackOverlayandTracerouteMapContentmarker composables.rememberComposeBitmapDescriptorwithrememberEmojiMarkerDescriptor.The
Clusteringcomposable inNodeClusterMarkers.ktis intentionally untouched -- it uses the library's internalComposeUiClusterRendererwhich has its own lifecycle protection.Visual fidelity
The Canvas renderer matches the original
NodeChipcomposable:node.isIgnoredscaledDensityNotes for reviewers
Markercomposables.@OptIn(MapsComposeExperimentalApi::class)remains onNodeTrackOverlayforMarkerInfoWindowComposable(used for breadcrumb markers, not node chips).