MyFamilyTracker
Real-time family location sharing — Firebase Realtime DB for sub-second propagation, WorkManager + ForegroundService for OS-compliant background collection, geofencing via Google Maps API.
Adding Google Maps to a Compose app is straightforward once you know the Maps Compose library — markers, camera control, custom styling, and the performance and lifecycle gotchas that bite people first.
On this page
Maps used to mean wrestling a
MapViewFragmentThe core composable is
GoogleMaprememberCameraPositionStateval camera = rememberCameraPositionState {
position = CameraPosition.fromLatLngZoom(LatLng(13.75, 100.50), 12f)
}
GoogleMap(
modifier = Modifier.fillMaxSize(),
cameraPositionState = camera,
) {
Marker(state = MarkerState(position = LatLng(13.75, 100.50)), title = "Bangkok")
}Markers, polylines, and circles are child composables of
GoogleMapThe most common "why is my map blank" is the API key. The key goes in the manifest, but the key string itself should come from a gitignored properties file, never hardcoded, and the key must be restricted in the Google Cloud console to your app's package and signing certificate. An unrestricted key in a shipped APK is a billing liability — anyone can extract and use it. Restricting it to your app means a leaked key is useless to anyone else.
Because the camera is hoisted, animating it is just calling a suspend function. I tie camera moves to app events — a user taps a list item, the camera flies to that location.
LaunchedEffect(selected) {
selected?.let {
camera.animate(CameraUpdateFactory.newLatLngZoom(it.latLng, 15f))
}
}The thing to watch is not fighting the user. If you animate the camera on every state change, you'll yank it away while they're panning. I only drive the camera in response to explicit user intent, not on every recomposition.
A handful of markers is fine. Hundreds will make the map stutter and the UI thread sweat. The fix is clustering — grouping nearby markers into a single cluster marker that splits as the user zooms in. The Maps Compose utilities include clustering support, and switching to it is the difference between a smooth map and a janky one for any app that plots real datasets. Don't wait until it's slow; if you know you'll have many points, reach for clustering from the start.
The library handles most lifecycle wiring for you, which is a relief compared to the old
MapViewA default Google map looks like everyone else's. A custom map style — muted colors, hidden points of interest, a palette that matches your app — makes the screen feel like part of your product rather than an embedded widget. You define the style as a JSON resource and pass it via map properties. It's a small effort that disproportionately improves how polished the screen feels, and it's the kind of detail that separates an app that looks shipped from one that looks like a tutorial.
If there's one mindset that smooths the whole experience, it's remembering that the map is a piece of declarative UI like any other, not a special black box. The camera is hoisted state you control, the markers are composables you declare for the current data, and the same Compose habits that serve you elsewhere — keeping state in one place, reacting to it rather than imperatively poking the view — apply directly. People get into trouble when they treat the map as a foreign object and start fighting it imperatively. Lean into the declarative model, restrict your key, cluster your markers, and the map becomes one of the more pleasant screens to build rather than the dreaded one.
rememberCameraPositionStateSudarshan Chaudhari
AI Systems Builder / Product Engineer
Bangkok, Thailand
Solo Android developer with 13+ years in QA, building Android apps, AI automation systems, and developer tools at SudarshanTechLabs.
Related Posts
Related Apps
Real-time family location sharing — Firebase Realtime DB for sub-second propagation, WorkManager + ForegroundService for OS-compliant background collection, geofencing via Google Maps API.
Building something? Available for Android dev and QA consulting.
Work with meComments — powered by Giscus
Real-time family location sharing — Firebase Realtime DB for sub-second propagation, WorkManager + ForegroundService for OS-compliant background collection, geofencing via Google Maps API.
ReadPrivate dream journal — structured entry capture, pattern tagging, and optional Claude-powered insight generation. All data stays on-device by default.
ReadWorkout tracker — exercise logging with set/rep/weight history, goal progression, and local Room DB persistence. No account, no cloud sync required.
Read