Building KegalFlow: Privacy-First Wellness Training With Explainable Adaptive Pacing
Aug 2026 · 3 min read

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