[go: nahoru, domu]

Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[GoogleMap]: Markers aren't cleared out when provided backing field changes #456

Open
bmc08gt opened this issue Nov 10, 2023 · 7 comments
Open
Labels
needs more info This issue needs more information from the customer to proceed. type: bug Error or flaw in code with unintended results or allowing sub-optimal usage patterns.

Comments

@bmc08gt
Copy link
bmc08gt commented Nov 10, 2023

Environment details

Kotlin 1.9.10
AGP 8.3.0-alpha11
Maps Compose 4.1.1
Compose Plugin 1.5.10

Steps to reproduce

  1. Add a GoogleMap() composable and render MarkerComposables from a List provided by a ViewModel State or other data source
  2. Change the List contents
  3. Observe the contents within the markers change but not the positions

Code example

@Composable
actual fun MapView(
    modifier: Modifier,
    contentPadding: PaddingValues,
    userLocation: LatLong?,
    resultLocations: List<Stay.Minimal>,
    useTotalPrice: Boolean,
    onMarkerSelectionChange: (String?) -> Unit,
    onMapMoved: () -> Unit,
) {
    [...]

    BoxWithConstraints(modifier = modifier) {
        GoogleMap(
            modifier = Modifier.matchParentSize(),
            contentPadding = contentPadding,
            contentDescription = "Map view for properties with applied filters and search parameters",
            cameraPositionState = cameraPositionState,
            properties = properties,
            uiSettings = uiSettings,
             currentSelectedMarkerId = null }
        ) {
            resultLocations.forEach { listing ->
                val location = LatLng(listing.location.latitude, listing.location.longitude)
                val markerState = rememberMarkerState(
                    position = location
                )
                MarkerComposable(
                    keys = arrayOf(
                        listing.id,
                        location,
                        useTotalPrice,
                        currentSelectedMarkerId ?: "",
                        previousSelectedMarkers
                    ),
                    state = markerState,
                    tag = listing.id,
                    
                        currentSelectedMarkerId = it.tag as? String
                        true
                    }
                ) {
                    PricePill(
                        price = "${listing.totalPriceOfStay()}".formatAsMoney(),
                        isSelected = currentSelectedMarkerId == listing.id,
                        wasPreviouslySelected = previousSelectedMarkers.contains(listing.id)
                    )
                }
            }

            [...]
        }
    }
}

However by creating my own MutableState for the List, initialized as emptyList() I'm able to have the map clear and redraw the new markers.

var markersToDraw by remember(resultLocations) {
      mutableStateOf(emptyList<Stay.Minimal>())
}
GoogleMap(
    modifier = Modifier.matchParentSize(),
    contentPadding = contentPadding,
    contentDescription = "Map view for properties with applied filters and search parameters",
    cameraPositionState = cameraPositionState,
    properties = properties,
    uiSettings = uiSettings,
     currentSelectedMarkerId = null }
) {
    markersToDraw.forEach { listing ->
        val location = LatLng(listing.location.latitude, listing.location.longitude)
        val markerState = rememberMarkerState(
            position = location
        )
        MarkerComposable(
            keys = arrayOf(
                listing.id,
                location,
                useTotalPrice,
                currentSelectedMarkerId ?: "",
                previousSelectedMarkers
            ),
            state = markerState,
            tag = listing.id,
            
                currentSelectedMarkerId = it.tag as? String
                true
            }
        ) {
            PricePill(
                price = "${listing.totalPriceOfStay()}".formatAsMoney(),
                isSelected = currentSelectedMarkerId == listing.id,
                wasPreviouslySelected = previousSelectedMarkers.contains(listing.id)
            )
        }
    }

    [...]

    LaunchedEffect(resultLocations) {
        markersToDraw = resultLocations
    }
}

Stack trace

N/A

Following these steps will guarantee the quickest resolution possible.

Thanks!

@bmc08gt bmc08gt added triage me I really want to be triaged. type: bug Error or flaw in code with unintended results or allowing sub-optimal usage patterns. labels Nov 10, 2023
@wangela
Copy link
Member
wangela commented Nov 10, 2023

If you would like to upvote the priority of this issue, please comment below or react on the original post above with 👍 so we can see what is popular when we triage.

@bmc08gt Thank you for opening this issue. 🙏
Please check out these other resources that might help you get to a resolution in the meantime:

This is an automated message, feel free to ignore.

@kikoso
Copy link
Collaborator
kikoso commented Nov 13, 2023

Hi @bmc08gt ,

Your approach is correct. We have this sample here that provides a snippet showcasing how to update individual markers.

Maybe a question on the snippet above: are you sure the keys are unique on each update? They should trigger the recomposition.

@bubenheimer
Copy link
Contributor

@bmc08gt I believe the most significant problem is your usage of rememberMarkerState(). You never actually update the MarkerState location. The rememberMarkerState() position parameter is only used for state initialization, not state updates.

@kikoso kikoso added priority: p4 An issue that should be addressed eventually. needs more info This issue needs more information from the customer to proceed. and removed triage me I really want to be triaged. priority: p4 An issue that should be addressed eventually. labels Nov 13, 2023
@bmc08gt
Copy link
Author
bmc08gt commented Nov 13, 2023

@bmc08gt I believe the most significant problem is your usage of rememberMarkerState(). You never actually update the MarkerState location. The rememberMarkerState() position parameter is only used for state initialization, not state updates.

Im creating entirely new markers with new coordinates (each with their own MarkerState). The markers were being reused even though I was reiterating through an entirely new list with new unique identifiers. They isn't always going to be the same amount of markers every time being rendered in the map (list is filtered results).

@oblakr24
Copy link
oblakr24 commented Nov 20, 2023

I am having the same issue. Basically I move the map with a new set of markers to display. At some point (after a couple of moves) the markers don't update anymore. They're still showing in the old position, even though the Map was recomposed with the new positions and I can see the marker contents were recomposed as well.
The fix with a backing mutable state works for me as well.

Edit: they key (ID) given to the MarkerComposable is unique every time, per every distinct marker.

@LozariaVinok
Copy link

I have the same issue.

I try to display one marker at a time. When clicking on the map, I attempt to update both the marker's position and its displayed text. While the position updates successfully, the text remains unchanged.

Code example:
`

    var markerPosition: LatLng? by remember {
            mutableStateOf(null)
        }
        var counter by remember {
            mutableStateOf(0)
        }
        GoogleMap(
            modifier = Modifier.fillMaxSize(),
            cameraPositionState = cameraPositionState,
            
                markerPosition = it
                counter++
            }) {
            markerPosition?.let {
                MarkerComposable(state = MarkerState(position = it)) {
                    Card {
                        Text(text = "marker $counter")
                    }
                }
            }
       }

`

I tried to migrate to MarkerComposable from solution with Bitmap descriptor. Unfortunately, I can no longer use the previous solution because, after a library update (not precisely sure which update), it results in constant flickering of the Marker, and it makes impossible to process click events on the marker.

@bubenheimer
Copy link
Contributor
bubenheimer commented Jan 2, 2024

@bmc08gt in your original example, do you reuse the List object and simply clear and completely repopulate it when you send updates? If so, it may help to use a completely new List object for your "entirely new list".

What I believe is happening: Compose tries to align the old contents of the list with the new contents, and trying to reuse whatever it composed before, in particular remembered MarkerState. Because you never tell Compose to update the marker positions, you get new Marker contents, but old positions.

If it does not work despite using a new List object every time, additionally wrap your forEach loop with the new List object as a key:

key(resultLocations) {
    resultLocations.forEach {...}
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
needs more info This issue needs more information from the customer to proceed. type: bug Error or flaw in code with unintended results or allowing sub-optimal usage patterns.
Projects
None yet
Development

No branches or pull requests

6 participants