Helio: A Solar-Anchored Circadian Rhythm App for Digital Nomads

The problem

Most circadian rhythm and sleep advice assumes you live in one place, in one time zone, on a fixed routine. As a digital nomad myself, that assumption breaks down constantly — sunrise in Phnom Penh isn’t sunrise in Medellín, and a “get sunlight by 8am” rule means nothing a week after you’ve changed continents.

I built Helio to solve my own problem: a schedule that anchors to the sun instead of the clock, and updates itself automatically as I travel.

Helio on the App Store · Helio on Google Play · Source on GitHub

What it does

Helio uses the device’s GPS to calculate the user’s exact local sunrise and sunset, then derives four daily cues from those two anchors:

  • Morning sunlight — sunrise to sunrise + 3 hours
  • Afternoon sunlight — 2 hours before sunset
  • Caffeine cutoff — 4 hours before sunset
  • Dim the lights — 2 hours after sunset

Every cue includes a short, plain-language explanation of the biology behind it, and the whole schedule recalculates automatically whenever the user opens the app from a new location — no manual timezone input required.

Technical overview

  • Framework: Flutter / Dart, targeting iOS and Android from a single codebase
  • Architecture: MVVM with a repository layer — LocationRepository and SunTimesRepository handle GPS and solar calculation independently, ScheduleViewModel orchestrates them and exposes a single sealed-class state (ScheduleLoading / ScheduleSuccess / ScheduleError) to the UI
  • Solar calculation: done fully on-device using astronomical algorithms (solar_calculator), so the app works offline and never sends location data anywhere
  • Custom rendering: the home screen’s sky and sun arc are hand-drawn with Flutter’s CustomPainter API — a gradient sky that interpolates through color keyframes across the day, a quadratic Bézier arc, and a sun position computed from that same curve
  • Notifications: local, on-device scheduling via flutter_local_notifications, with a 2-day scheduling window and a self-cancelling “nudge” notification for users who haven’t opened the app recently
  • Dependency injection: get_it
  • Reverse geocoding, timezone handling: geocoding and timezone/flutter_timezone

Notable engineering challenges

A stubborn timezone bug. Early on, computed sunrise/sunset times were coming back wildly wrong — off by 7, then 12, then 5 hours, depending on the fix attempted. The root cause turned out to be a third-party library returning a UTC timestamp with a misleading local-offset label. The real fix was reconstructing a local DateTime directly from the UTC components plus a manually applied offset — the kind of bug that only reveals itself by printing every intermediate value and reasoning through the conversion chain step by step.

Building a physics-adjacent UI from scratch. The sky visualization has no off-the-shelf equivalent — I worked through the quadratic Bézier point formula directly to place the sun, the cue markers, and their labels all on the same curve, then layered a continuously-interpolated sky gradient and a fade-out for the sun as it approaches the horizon.

A production Android bug that five rejections couldn’t diagnose from logs alone. The App Store release went smoothly, but Google Play rejected the Android build four times with only “the app doesn’t load” as feedback — no stack trace, no device info. I eventually bought a physical Android test device (my only testing had been iOS-only until then), ran the app in debug mode, and found the real cause in under a minute: flutter_local_notifications requires explicit Android initialization settings, and mine only configured iOS. One missing constructor argument was crashing the app on launch, before a single frame ever rendered — invisible in every log Google’s review process surfaced, but immediately obvious with a real device and a debug console. It was a good reminder that some bugs only exist at the intersection of platform and hardware, and no amount of code review finds them without actually running the code where it matters.

Store compliance, end to end. Beyond the code, I set up both developer accounts (including an Apple Organization enrollment tied to an LLC), wrote the privacy policy, configured App Privacy / Data Safety declarations on both platforms, handled code signing and provisioning for iOS, set up Play App Signing and a release keystore for Android, and navigated both stores’ review processes to a live release.

Result

Helio is live on the App Store and Google Play, free, with no account required and no data collected beyond an on-device, momentary GPS read. It’s built for an audience I understand personally, and it’s the first app I’ve taken completely from idea through production release on both major mobile platforms.