Skip to content

fix(map): scope cluster-renderer ViewTreeLifecycleOwner to map host view#5708

Merged
jamesarich merged 1 commit into
mainfrom
claude/musing-lehmann-386f64
Jun 1, 2026
Merged

fix(map): scope cluster-renderer ViewTreeLifecycleOwner to map host view#5708
jamesarich merged 1 commit into
mainfrom
claude/musing-lehmann-386f64

Conversation

@jamesarich

Copy link
Copy Markdown
Collaborator

Problem

The top FATAL crash on the google flavor of 2.7.14 is com.google.maps.android.compose.clustering.ComposeUiClusterRenderer.renderViewToBitmapDescriptorIllegalStateException: Composed into the View which doesn't propagate ViewTreeLifecycleOwner!. It is still present in build 29321006, i.e. after #5704.

maps-compose renders each non-clustered item to a bitmap through an off-screen ComposeView that it attaches under the MapView (ComposeUiClusterRenderer + NoDrawContainerView). That ComposeView walks up the view tree for a ViewTreeLifecycleOwner; when it finds none it crashes. There is no upstream fix as of maps-compose 8.3.0 (latest) — tracked unresolved as googlemaps/android-maps-compose#875 / #325.

#5704 removed the previous workaround because it attached a transient NavEntry lifecycle to the activity root, which caused the node-list popup 0×0 regression (#5684).

Fix

Re-introduce owner propagation, but scoped to LocalView.current (the map screen's host view, an ancestor of the internally-created MapView) instead of view.rootView:

  • Sets ViewTreeLifecycleOwner + SavedStateRegistryOwner on the map host view so the renderer's internal ComposeView can resolve them.
  • Captures and restores the previous owners on dispose, so Popups/DropdownMenus are left untouched (avoids reopening fix: address top Crashlytics crashes and non-fatals for build 29320984 #5684).
  • Keeps a Lifecycle.STARTED guard to skip the cluster renderer's async-Handler render after the screen has stopped.

Clustering(...), clusterItemContent, and the precision-circle clusterItemDecoration are unchanged — no feature regression.

Caveats

  • This is a mitigation, not a hermetic seal: the async-Handler-after-dispose tail can only be fully closed by removing the library's ComposeView path entirely (a Canvas custom-renderer rewrite, which would require reimplementing the precision-circle decoration). Filed as a possible follow-up.
  • Compile-verified (spotlessCheck, detekt, :androidApp:compileGoogleDebugKotlin all pass) but not runtime-verified here (needs a Maps key + real google-services.json). Suggest a smoke test: zoom to form/break clusters on the map, then navigate back and open a node-list popup to confirm popups still render.

🤖 Generated with Claude Code

maps-compose renders each non-clustered item to a bitmap through an
off-screen ComposeView attached under the MapView (ComposeUiClusterRenderer
+ NoDrawContainerView). That ComposeView walks the view tree for a
ViewTreeLifecycleOwner and, finding none, crashes with "Composed into the
View which doesn't propagate ViewTreeLifecycleOwner!"
(googlemaps/android-maps-compose#875 / #325) — a FATAL on the map screen
still present in 2.7.14 (29321006), i.e. after #5704.

#5704 removed the prior workaround because it attached a transient NavEntry
lifecycle to the activity root, causing the node-list popup 0x0 regression
(#5684). Re-introduce owner propagation but scope it to LocalView.current
(the map host view, an ancestor of the internal MapView) instead of
view.rootView, and restore the previous owners on dispose so Popups and
DropdownMenus are left untouched. Keep a Lifecycle.STARTED guard to skip the
cluster renderer's async-Handler render after the screen has stopped.

Clustering, clusterItemContent, and the precision-circle decoration are
unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@jamesarich jamesarich enabled auto-merge June 1, 2026 22:25
@github-actions github-actions Bot added the bugfix PR tag label Jun 1, 2026
@jamesarich jamesarich added this pull request to the merge queue Jun 1, 2026
Merged via the queue into main with commit 1b66173 Jun 1, 2026
18 checks passed
@jamesarich jamesarich deleted the claude/musing-lehmann-386f64 branch June 1, 2026 22:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bugfix PR tag

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant