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

Setting CameraPositionState.position is not stateful #523

Open
bubenheimer opened this issue Feb 3, 2024 · 2 comments
Open

Setting CameraPositionState.position is not stateful #523

bubenheimer opened this issue Feb 3, 2024 · 2 comments
Labels
triage me I really want to be triaged. type: bug Error or flaw in code with unintended results or allowing sub-optimal usage patterns.

Comments

@bubenheimer
Copy link
Contributor

android-maps-compose 4.3.3

Setting CameraPositionState.position is not stateful. (Setting the property calls through to the GoogleMap SDK and immediately changes the camera position on the map.) This is an invalid Compose architectural decision:

  1. Reading this property is stateful: it reads from a state.
  2. The class has State in its name, and position is its primary public property, so setting it must be stateful to not completely defy user expectations.
  3. Not even the KDoc mentions setting not being stateful.
  4. By contrast, setting MarkerState.position is stateful. The behavior of CameraPositionState is different when it should be the same.

The general impact is that setting it via snapshot state does not work correctly. This is a fundamental violation of normal Compose behavior.

For example, a user might attempt something like this to control camera position, which mimics the behavior of rememberUpdatedState(), but for CameraPositionState instead of MutableState:

@Composable
fun MapWithCamera(cameraPosition: CameraPosition) {
    val cameraState = rememberCameraPositionState(position = cameraPosition)
        .also { it.position = cameraPosition }

    GoogleMap(
        cameraPositionState = cameraState
    )
}

This approach is normally sound, but not valid here, because setting CameraStatePosition.position is a side effect (irreversible), instead of setting snapshot state. For example, if the composition is cancelled, the new camera position remains set.

CameraPositionState.move() can only be called from the main thread, so disallowing setter access in favor of this method is not a great workaround.

/**
* Current position of the camera on the map.
*/
public var position: CameraPosition
get() = rawPosition
set(value) {
synchronized(lock) {
val map = map
if (map == null) {
rawPosition = value
} else {
map.moveCamera(CameraUpdateFactory.newCameraPosition(value))
}
}
}

@bubenheimer bubenheimer 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 Feb 3, 2024
@bubenheimer
Copy link
Contributor Author

bubenheimer commented Feb 3, 2024

Another consequence: updating padding and camera position together is not possible - camera will always be set instantly, then eventually padding when applying composition. This means that the camera will not be centered within the updated padding.

If CameraPositionState.position was backed by MutableState, then the API could update padding and camera in the appropriate order.

This is related to #142

@CaolanCode
Copy link

This issue remains unresolved.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
triage me I really want to be triaged. 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

2 participants