last known location Kotlin Flow
Ever wondered how your favorite apps always know where you are? Whether it’s guiding you to the nearest taco joint or tracking your morning run, location services are the unsung heroes of mobile development. As a developer, getting a handle on these features can level up your app game big time. Today, we’re diving into how to snag the last known location and keep those updates flowing using Kotlin Flow in Android, Kotlin Multiplatform (KMP), and Compose Multiplatform (CMP). I’ll throw in some code snippets, sprinkle a bit of humor, and make sure you walk away ready to tackle location-based projects like a pro.
Let’s jump right in!
Location services in Android give your app the power to pinpoint where the user is, thanks to tools like the Fused Location Provider API from Google Play Services. Pair that with Kotlin Flow—a slick way to handle streams of data—and you’ve got a recipe for smooth, reactive location handling. Flow is part of Kotlin’s coroutines toolkit, perfect for dealing with stuff like location updates that keep coming at you over time.
Picture this: you’re coding an app to track your dog’s wild adventures in the park. You need his location pronto, and you want updates as he chases squirrels. Kotlin Flow makes this a breeze by letting you treat location data like a stream you can tap into whenever you need. Let’s see how to make it happen.
Before we get to the fun stuff, we’ve got to lay the groundwork. That means adding the right dependencies and sorting out permissions. Here’s what you need in your Android build.gradle
file:
implementation "com.google.android.gms:play-services-location:21.0.1"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-play-services:1.6.4"
These are the latest versions as of now—keep an eye on the Google Maven repository for updates. For KMP or CMP, you might tweak these depending on your platforms, but we’ll get to that later.
Next, pop these lines into your AndroidManifest.xml
:
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
Permissions aren’t just a formality—Android won’t hand over location data unless you ask nicely at runtime too. We’ll handle that in the code soon. For now, let’s assume your project’s ready to roll.
Alright, let’s grab that last known spot where your user (or their dog) was hanging out. We’ll use the Fused Location Provider API, which is fast and reliable. Since this is a one-time grab, a suspend function fits the bill perfectly:
suspend fun getLastKnownLocation(context: Context): Location? {
val fusedLocationClient = LocationServices.getFusedLocationProviderClient(context)
return try {
fusedLocationClient.lastLocation.await()
} catch (e: SecurityException) {
Log.e("LocationError", "Permission denied, whoops!", e)
null
}
}
Call this from a coroutine scope, like in your activity:
lifecycleScope.launch {
val location = getLastKnownLocation(this@MainActivity)
location?.let {
println("Last seen at: ${it.latitude}, ${it.longitude}")
} ?: run {
println("Location’s playing hide and seek—check permissions!")
}
}
Quick heads-up: if you forget permissions, you’ll be staring at null
for hours, wondering why the universe hates you. Been there, done that—save yourself the headache and double-check!
Now for the real magic: getting location updates as they happen. This is where Kotlin Flow shines. First, set up a location request to tell the system how often you want updates:
val locationRequest = LocationRequest.create().apply {
interval = 10000 // 10 seconds
fastestInterval = 5000 // 5 seconds
priority = LocationRequest.PRIORITY_HIGH_ACCURACY
}
Then, whip up a Flow to stream those updates:
fun locationFlow(context: Context): Flow<Location> = callbackFlow {
val fusedLocationClient = LocationServices.getFusedLocationProviderClient(context)
val locationCallback = object : LocationCallback() {
override fun onLocationResult(locationResult: LocationResult) {
for (location in locationResult.locations) {
trySend(location).isSuccess // Send each new location downstream
}
}
}
fusedLocationClient.requestLocationUpdates(
locationRequest,
locationCallback,
Looper.getMainLooper()
)
awaitClose {
fusedLocationClient.removeLocationUpdates(locationCallback) // Clean up when done
}
}
To use this in your app, collect the Flow like so:
lifecycleScope.launch {
locationFlow(this@MainActivity).collect { location ->
println("New spot: ${location.latitude}, ${location.longitude}")
// Update your UI or save the data—sky’s the limit!
}
}
The awaitClose
block ensures you’re not sucking up battery life when the Flow’s cancelled—like when your user closes the app to avoid your terrible dog-tracking UI (kidding, I’m sure it’s great).
You can’t just grab locations without asking. Here’s how to request permissions using the Activity Result API:
val locationPermissionRequest = registerForActivityResult(
ActivityResultContracts.RequestMultiplePermissions()
) { permissions ->
when {
permissions[Manifest.permission.ACCESS_FINE_LOCATION] == true -> {
println("Permission granted—let’s roll!")
lifecycleScope.launch {
locationFlow(this@MainActivity).collect { location ->
println("Location: ${location.latitude}, ${location.longitude}")
}
}
}
else -> {
println("Permission denied. No location for you!")
// Maybe show a sad puppy GIF
}
}
}
locationPermissionRequest.launch(arrayOf(Manifest.permission.ACCESS_FINE_LOCATION))
This checks for fine location access, but you could add coarse location too if you’re feeling generous. Always test this flow—nothing’s worse than an app that crashes because you assumed permissions were a done deal.
If you’re going multiplatform, things get a bit spicier. In KMP, you’re sharing code between Android and iOS, but location APIs are platform-specific. Android uses Fused Location Provider, while iOS leans on Core Location. In CMP, you’re adding Compose UI into the mix, but the location logic stays similar.
Here’s a quick breakdown:
Platform | Location API | Kotlin Flow Integration |
---|---|---|
Android | Fused Location Provider | Use callbackFlow with LocationCallback |
iOS (KMP) | Core Location | Use callbackFlow with CLLocationManager |
CMP | Platform-specific | Wrap platform APIs in Flow |
For KMP, you can abstract this with an expect
/actual
setup. In your common code:
expect fun locationFlow(): Flow<Location>
Android actual:
actual fun locationFlow(): Flow<Location> = // Same as above with Fused Location
iOS actual (pseudo-Kotlin):
actual fun locationFlow(): Flow<Location> = callbackFlow {
val manager = CLLocationManager()
manager.delegate = // Custom delegate sending locations to trySend
manager.startUpdatingLocation()
awaitClose { manager.stopUpdatingLocation() }
}
This keeps your shared code clean while letting platform-specific magic happen behind the scenes. CMP follows the same pattern—just add Compose UI on top.
Let’s wrap up with some hard-earned wisdom:
interval
and priority
to match your needs.LocationRepository
class. It’s cleaner and easier to test:class LocationRepository(private val client: FusedLocationProviderClient) {
fun locationFlow(): Flow<Location> = // Flow implementation
}
awaitClose
helps, but double-check your lifecycle.Once, I forgot to stop updates and drained my test phone’s battery in an hour. My app was basically a vampire—don’t let yours be one too!
There you have it—a crash course in grabbing the last known location and streaming updates with Kotlin Flow. We’ve covered the basics for Android, peeked at KMP and CMP, and thrown in some code to get you started. Whether you’re tracking pets, plotting runs, or just messing around, this setup’s got you covered.
Take these examples, tweak them, and build something awesome. If you get stuck, just imagine your location updates as little breadcrumbs leading you back to sanity. Go code something great—I’m rooting for you!
Happy coding!
Introduction: Transform Your Cross-Platform Development with Material Design 3 Are you ready to revolutionize your… Read More
Jetpack Compose 1.8 rolls out handy features like Autofill integration, slick Text enhancements including auto-sizing… Read More
Reified Keyword in Kotlin: Simplify Your Generic Functions Kotlin's reified keyword lets your generic functions know the… Read More
Android Studio Cloud: Ditch the Setup, Code Anywhere (Seriously!) Alright, fellow Android devs, gather 'round… Read More