MarkerState
MarkerState manages the configuration and behavior of markers on the map. It provides reactive properties that automatically update the marker’s appearance and position when modified.
Constructor
Section titled “Constructor”MarkerState( position: GeoPoint, id: String? = null, extra: Serializable? = null, icon: MarkerIcon? = null, animation: MarkerAnimation? = null, clickable: Boolean = true, draggable: Boolean = false)Properties
Section titled “Properties”Core Properties
Section titled “Core Properties”id: String: Unique identifier for the marker (auto-generated if not provided)position: GeoPoint: Current geographic position of the markerextra: Serializable?: Additional data attached to the marker
Visual Properties
Section titled “Visual Properties”icon: MarkerIcon?: Custom icon for the marker (uses default if null)clickable: Boolean: Whether the marker responds to click eventsdraggable: Boolean: Whether the marker can be dragged by the user
Interactive Properties
Section titled “Interactive Properties”isDragging: Boolean(read-only): Current dragging stateinternalPosition: GeoPoint(read-only): Position used for rendering (accounts for drag state)
Methods
Section titled “Methods”Animation
Section titled “Animation”fun setAnimation(animation: MarkerAnimation?)Sets or clears the marker animation.
internal fun getAnimation(): MarkerAnimation?Gets the current animation (internal use).
State Management
Section titled “State Management”fun copy( id: String? = this.id, position: GeoPoint = this.position, extra: Serializable? = this.extra, icon: MarkerIcon? = this.icon, clickable: Boolean? = this.clickable, draggable: Boolean? = this.draggable): MarkerStateCreates a copy of the marker state with modified properties.
Reactive Updates
Section titled “Reactive Updates”fun asFlow(): Flow<MarkerFingerPrint>Returns a flow that emits when the marker state changes.
fun fingerPrint(): MarkerFingerPrintCreates a fingerprint for efficient change detection.
Usage Examples
Section titled “Usage Examples”Basic MarkerState
Section titled “Basic MarkerState”val markerState = MarkerState( position = GeoPointImpl.fromLatLong(37.7749, -122.4194), extra = "San Francisco marker")
// Replace MapView with your chosen map provider, such as GoogleMapView, MapboxMapViewMapView(state = mapViewState) { Marker(markerState)}Customized MarkerState
Section titled “Customized MarkerState”val customMarkerState = MarkerState( position = GeoPointImpl.fromLatLong(37.7749, -122.4194), icon = DefaultIcon( fillColor = Color.Blue, label = "SF", scale = 1.2f ), clickable = true, draggable = true, extra = "Draggable San Francisco marker")
// Replace MapView with your chosen map provider, such as GoogleMapView, MapboxMapViewMapView( state = mapViewState, onMarkerClick = { markerState -> println("Clicked marker: ${markerState.extra}") }, onMarkerDrag = { markerState -> println("Marker dragged to: ${markerState.position}") }) { Marker(customMarkerState)}Dynamic MarkerState Updates
Section titled “Dynamic MarkerState Updates”@Composablefun DynamicMarkerExample() { var markerState by remember { mutableStateOf( MarkerState( position = GeoPointImpl.fromLatLong(37.7749, -122.4194), icon = DefaultIcon(fillColor = Color.Red, label = "1"), extra = "Counter: 1" ) ) }
var counter by remember { mutableStateOf(1) }
Column { Button( onClick = { counter++ markerState = markerState.copy( icon = DefaultIcon( fillColor = Color.Blue, label = counter.toString() ), extra = "Counter: $counter" ) } ) { Text("Update Marker ($counter)") }
// Replace MapView with your chosen map provider, such as GoogleMapView, MapboxMapViewMapView(state = mapViewState) { Marker(markerState) } }}MarkerState with Position Animation
Section titled “MarkerState with Position Animation”@Composablefun AnimatedMarkerExample() { val startPosition = GeoPointImpl.fromLatLong(37.7749, -122.4194) val endPosition = GeoPointImpl.fromLatLong(37.7849, -122.4094)
var markerState by remember { mutableStateOf( MarkerState( position = startPosition, icon = DefaultIcon(fillColor = Color.Green, label = "Moving"), extra = "Animated marker" ) ) }
LaunchedEffect(Unit) { while (true) { delay(2000) markerState = markerState.copy( position = if (markerState.position == startPosition) endPosition else startPosition ) } }
// Replace MapView with your chosen map provider, such as GoogleMapView, MapboxMapViewMapView(state = mapViewState) { Marker(markerState) }}MarkerState with Drag Handling
Section titled “MarkerState with Drag Handling”@Composablefun DraggableMarkerExample() { var markerState by remember { mutableStateOf( MarkerState( position = GeoPointImpl.fromLatLong(37.7749, -122.4194), icon = DefaultIcon( fillColor = Color.Purple, label = "Drag", scale = 1.2f ), draggable = true, extra = "Draggable marker" ) ) }
Column { Text("Marker Position:") Text("Lat: ${markerState.position.latitude}") Text("Lng: ${markerState.position.longitude}") Text("Is Dragging: ${markerState.isDragging}")
// Replace MapView with your chosen map provider, such as GoogleMapView, MapboxMapViewMapView( state = mapViewState, onMarkerDragStart = { draggedMarker -> println("Drag started: ${draggedMarker.id}") }, onMarkerDrag = { draggedMarker -> if (draggedMarker.id == markerState.id) { markerState = markerState.copy(position = draggedMarker.position) } }, onMarkerDragEnd = { draggedMarker -> println("Drag ended: ${draggedMarker.id}") } ) { Marker(markerState) } }}Multiple MarkerStates with Different Behaviors
Section titled “Multiple MarkerStates with Different Behaviors”@Composablefun MultipleMarkersExample() { val markerStates = remember { listOf( MarkerState( position = GeoPointImpl.fromLatLong(37.7749, -122.4194), icon = DefaultIcon(fillColor = Color.Red, label = "Static"), clickable = true, draggable = false, extra = "Static marker" ), MarkerState( position = GeoPointImpl.fromLatLong(37.7849, -122.4094), icon = DefaultIcon(fillColor = Color.Green, label = "Drag"), clickable = true, draggable = true, extra = "Draggable marker" ), MarkerState( position = GeoPointImpl.fromLatLong(37.7649, -122.4294), icon = DefaultIcon(fillColor = Color.Blue, label = "Info"), clickable = true, draggable = false, extra = "Information marker with long description" ) ) }
var selectedMarker by remember { mutableStateOf<MarkerState?>(null) }
Column { selectedMarker?.let { marker -> Card(modifier = Modifier.padding(8.dp)) { Column(modifier = Modifier.padding(16.dp)) { Text("Selected: ${marker.extra}") Text("Position: ${marker.position.latitude}, ${marker.position.longitude}") Text("Draggable: ${marker.draggable}") } } }
// Replace MapView with your chosen map provider, such as GoogleMapView, MapboxMapViewMapView( state = mapViewState, onMarkerClick = { clickedMarker -> selectedMarker = clickedMarker }, onMapClick = { selectedMarker = null } ) { markerStates.forEach { markerState -> Marker(markerState) } } }}MarkerState with Custom Icons
Section titled “MarkerState with Custom Icons”@Composablefun CustomIconMarkerExample() { val context = LocalContext.current
val markerStates = remember { listOf( MarkerState( position = GeoPointImpl.fromLatLong(37.7749, -122.4194), icon = DefaultIcon( scale = 1.5f, fillColor = Color.Red, strokeColor = Color.White, strokeWidth = 2.dp, label = "XL" ), extra = "Large default icon" ), MarkerState( position = GeoPointImpl.fromLatLong(37.7849, -122.4094), icon = AppCompatResources.getDrawable(context, R.drawable.custom_pin)?.let { drawable -> DrawableDefaultIcon( backgroundDrawable = drawable, scale = 1.2f ) }, extra = "Custom drawable icon" ), MarkerState( position = GeoPointImpl.fromLatLong(37.7649, -122.4294), icon = AppCompatResources.getDrawable(context, R.drawable.weather_icon)?.let { drawable -> ImageIcon( drawable = drawable, anchor = Offset(0.5f, 1.0f) ) }, extra = "Weather image icon" ) ) }
// Replace MapView with your chosen map provider, such as GoogleMapView, MapboxMapViewMapView(state = mapViewState) { markerStates.forEach { markerState -> Marker(markerState) } }}State Observation
Section titled “State Observation”Observing MarkerState Changes
Section titled “Observing MarkerState Changes”@Composablefun MarkerStateObserver() { val markerState = remember { MarkerState( position = GeoPointImpl.fromLatLong(37.7749, -122.4194), extra = "Observed marker" ) }
// Observe state changes val markerFingerprint by markerState.asFlow().collectAsState( initial = markerState.fingerPrint() )
LaunchedEffect(markerFingerprint) { println("Marker state changed: $markerFingerprint") }
// Replace MapView with your chosen map provider, such as GoogleMapView, MapboxMapViewMapView(state = mapViewState) { Marker(markerState) }}Performance Considerations
Section titled “Performance Considerations”Efficient State Updates
Section titled “Efficient State Updates”// Good: Update properties directlymarkerState.position = newPositionmarkerState.icon = newIcon
// Good: Use copy for multiple changesval updatedState = markerState.copy( position = newPosition, icon = newIcon, extra = newExtra)
// Avoid: Creating new states unnecessarily// This creates a new state object each timeval newState = MarkerState(position = newPosition, icon = newIcon)Memory Management
Section titled “Memory Management”// Store marker states efficientlyval markerStates = remember { mutableStateMapOf<String, MarkerState>() }
// Add markermarkerStates[markerId] = MarkerState(...)
// Update markermarkerStates[markerId]?.let { state -> state.position = newPosition}
// Remove markermarkerStates.remove(markerId)Best Practices
Section titled “Best Practices”- Use Meaningful IDs: Provide custom IDs for markers you need to reference later
- Store Useful Data: Use the
extraproperty to store data needed for event handling - Reactive Updates: Leverage Compose’s reactive system - modify properties directly rather than recreating states
- Performance: Use
copy()when updating multiple properties simultaneously - Memory: Clean up marker states that are no longer needed
- Consistency: Maintain consistent icon styling across similar marker types
- User Feedback: Provide appropriate visual feedback for interactive markers
- Validation: Ensure position coordinates are valid before creating marker states