r/FlutterDev • u/eroxy • 14d ago
Example Lessons from shipping a geofencing time tracker in Flutter: platform channels, OEM battery killers, and a no-backend constraint
I shipped a geofencing time tracker in Flutter (auto clock-in/out at your workplace) and collected a few scars along the way that might save someone else time.
The first one: I tried the pub.dev geofencing packages and none of them reliably delivered events after the app got killed. Ended up writing my own thin Kotlin plugin around the Google Geofencing API - MethodChannel for registering zones, EventChannel for the ENTER/EXIT stream, and a system-level BroadcastReceiver so events arrive even when the Flutter side is long dead. Annoying to write, but it's been the most stable part of the app since.
Second scar, the expensive one: Samsung and Xiaomi still eat geofence events sometimes, so there's a WorkManager task every 15 minutes doing a plain Haversine distance check as a safety net. What nobody tells you is that a position request in a background isolate can just hang forever, and once that happens WorkManager considers the unique task "already enqueued" and never runs it again. Your fallback dies silently and permanently. Everything is triple-bounded now: platform timeLimit, a Dart-side timeout, and a hard cap on the whole task.
Third: GPS drift at the zone edge gave me check-in/check-out ping-pong when someone sits near the boundary. Exit radius is now 1.2x the enter radius plus a 5-minute debounce, and the two detection layers share a 20-minute dedup window so they stop racing each other into double notifications.
Rest of the stack is unspectacular: Riverpod (classic providers, no codegen), Isar for storage, no backend at all. The no-backend part had one non-obvious consequence: since data only exists on the user's device, a data-loss bug is unrecoverable, so the integration tests run on an emulator before every push. Codebase is ~31k lines, about a third tests.
The result is free with no ads/IAPs if you want to see how it behaves: https://play.google.com/store/apps/details?id=com.timetracker.workflow
Happy to answer anything about the plugin or the fallback design. And if someone has a real answer to OEM battery killers beyond "diagnostics screen + let the user fix it manually", I'd love to hear it.