Building KegalFlow: Privacy-First Wellness Training With Explainable Adaptive Pacing

Aug 2026 · 3 min read

kegal

I shipped KegalFlow — private pelvic-floor wellness and guided exercise training for iOS and Android.

Positioning is deliberate and narrow:

  • Is: a guided timer and training planner, with progress tracking and a transparent adaptive pacing helper, all on-device.
  • Is not: a diagnostic tool, a treatment, a medical device, a sexual-performance product, a social network, or an ad surface.

Copy rules are enforced in review: never treat / cure / prevent / diagnose / heal / fix / guaranteed / clinically proven / medical. Always general wellness language, and a standing pointer to a qualified professional for anything symptom-related.

That isn’t marketing polish. It’s product architecture.

Privacy as the default path

No account. No email capture on welcome. Onboarding ends with a blocking health disclaimer (acceptance timestamped) before Home.

Data stays on the device. The only intended network calls in v1 are store billing. Purchases use in_app_purchase, not RevenueCat — no third-party app-user identifier for a privacy label we intended to keep clean. Drift for the training database; export and delete live in settings.

Sessions that tell the truth when backgrounded

A kegel session is a state machine: prepare → contract → hold → release → rest → complete, with pause and early-exit paths.

The easy bug: accumulate progress with Timer.periodic. iOS suspends timers in background; Android throttles under Doze; you credit rounds the user never performed.

KegalFlow derives position from a monotonic clock:

now - startedAt - accumulatedPause + timelineShift

Wall clock is rejected (DST / manual time changes). Leaving the foreground suspends the run — if you can’t hear or feel cues, you’re not exercising. Long absences escalate to an explicit interrupted decision.

Adaptive Recovery Mode: rules you can read

After session feedback, Adaptive Recovery may propose a change to the next session.

Contract (enforced by domain tests):

  • Reads at most the last three sessions’ feedback
  • Adjusts only rest, rounds, and hold
  • Movement per step is bounded: rest ±5s, rounds ±1, hold ±1s
  • Output always clamped to a valid SessionConfig
  • Never increases intensity after pain or high fatigue
  • Always produces a non-empty explanation

Named rules include: pain reported, high fatigue, too hard, low completion, low control, ready to progress, steady.

Pain is highest precedence: progression blocks until cleared; safety notice is neutral and non-diagnostic.

The domain layer is pure Dart — no Flutter, no I/O, no DateTime.now() (clock injected). AdaptiveController only persists and sequences; it does not invent rules. The user must accept / adapt / reject a pending recommendation. Silent mutation of the plan is not allowed.

A clinician (or a skeptical App Reviewer) can read the whole rule set on one screen. That is the point — same spirit as iRun’s deterministic coach.

What I shipped

Flutter + Riverpod, feature-first. Home / Plan / Progress / Profile shell. Program library, reminders, streaks, paywall, help. Design-system widgets keep an internal “CoreFlow” codename in code; the user-facing brand is only KegalFlow.

Store links:

What I’d still harden

Keep language review ruthless as the app grows. If cross-device sync ever appears, revisit receipt validation without watering down “private by design.” Adaptive rules should stay boring and conservative — “smarter” is how wellness apps become unsafe claims.

Bet: in health-adjacent software, explainability and refusal-to-diagnose are features, not footnotes.

If you’re building wellness tools: put the rules in a testable file, require user consent for every plan change, and make the disclaimer blocking — not a checkbox buried in Settings.

  • flutter
  • wellness
  • privacy
  • explainable
  • local-first
  • health-tech