Women's Health · Fertility
momiplan 마미플랜
Cycle tracking, IVF records and heart-rate workout coaching for women preparing for pregnancy
- Client
- Healthcare startup (B2B2C · undisclosed)
- Period
- Jan 2026 — Jul 2026 · About 7 months
- Role
- Sole developer — Flutter app, Firebase backend, admin functions
- Team
- 1 engineer (me) + client product & design
- Platforms
- iOS · Android · Web (Admin)
- Status
- v1.0.0 (build 16) delivered, iOS & Android release builds
110 files
Dart
15K lines
Code
34 steps
Onboarding
1.0.0+16
Release build
Overview
Project overview
momiplan is a companion service for women undergoing fertility treatment or preparing for pregnancy. A 34-step onboarding survey — treatment stage, clinic, time trying, supplements, sleep issues — builds an understanding of each user, and menstrual cycles and IVF procedures are recorded in detail.
It combines BLE heart-rate-band workout coaching, a pregnancy-success dashboard and a lounge with clinics, columns and counselling bookings in one app — designed with an audit structure that shows who viewed what, as a service handling sensitive medical data should.
My Role
Responsibilities
- 01Designed and built the entire Flutter app (110 Dart files, ~15K lines)
- 02Google and Apple sign-in, four consent agreements, 34-step onboarding survey flow
- 03CustomPainter visualisations: custom calendar, cycle analysis, radar chart, heart gauge
- 04flutter_reactive_ble heart-rate-band scan / connect / reconnect and the workout-session state machine
- 05Firestore data model and security rules, Cloud Functions (Seoul region) admin API with audit logging
- 06Sequential iOS/Android build automation with automatic build-number bumping
Features
Key features
Cycle tracking
Start/end dates, cycle-length calculation, a custom calendar with month grid, day cells, tooltips and markers, and a cycle-length chart.
IVF procedure records
Dedicated screens per stage: ovarian stimulation (follicle-size slider, abdominal-discomfort score, symptoms), egg retrieval, fresh vs frozen embryo transfer.
Symptom logging
Intuitive SVG-icon logging of four bleeding levels, four discharge types and more.
Pregnancy-success dashboard
Heart gauge, percentile and a five-axis radar chart implemented directly with CustomPainter.
BLE heart-rate workout coaching
Scan → connect → countdown → monitoring → finish. Real-time 'good / adjust' feedback against the prescription (40-minute target, HR 100–140).
Lounge, counselling and plans
Articles, clinic / column / commerce cards, coordinator and psychological counselling bookings, a four-plan comparison screen (payments in a later phase).
Architecture
Design principles
Privacy-first data model
All user data lives only under users/{uid}, readable and writable by the owner alone via Firestore rules. Admin lists and audit logs are fully blocked from clients.
Admin API through audit logs
The admin dashboard never reads Firestore directly; it goes through callable Functions that write an access_logs entry for every query.
Time-series heart-rate storage
Per-second BPM is auto-saved every minute as one-minute chunk documents with zero-padded IDs. Seconds without signal are kept as null; the server stitches chunks into a CSV download.
Abnormal-exit recovery
On relaunch, sessions still marked inProgress are set to abnormal to keep data consistent.
Challenges
Engineering challenges
Two BLE permission models, one state
Problem
Android 12+ requires explicit BLUETOOTH_SCAN / CONNECT requests, while iOS has no permission API at all — the system prompts only once scanning starts.
Solution
Android requests explicitly with neverForLocation in the manifest to avoid location permission; iOS starts scanning immediately and listens for BleStatus.unauthorized. Both flows normalise into one Provider state.
Crashes when a screen closes mid-await
Problem
If a screen was disposed while awaiting scan or connect, notify was called on an already-disposed Provider and crashed.
Solution
A _disposed flag with a _safeNotify() pattern guards the lifecycle.
Reconnecting to a sensor that isn't advertising yet
Problem
A just-disconnected band has not resumed advertising, so a rescan cannot find it.
Solution
Try a direct connect with the last device id first, then fall back to scanning.
Apple only sends the name once
Problem
Sign in with Apple returns name and email only on the first authentication, leaving later profiles empty.
Solution
Persist at first authentication, combine surname + given name in Korean order, and hash the nonce with SHA-256.
A calendar that survives large font scaling
Problem
Accessibility font scaling broke the seven-column day selector and card alignment.
Solution
Cell size is capped by formula and card title line counts are fixed; the reasoning is kept in code comments.
Parallel iOS/Android builds collide
Problem
Building both platforms at once triggered 'Unable to delete .packages' from the flutter startup lock and ios/Flutter/ephemeral contention.
Solution
A sequential build script that warns when Xcode is running, bumps the build number only on success, copies the APK and opens the workspace automatically.
Tech Stack
Technology
App
- Flutter · Dart 3.10
- Material 3 · custom colour tokens
- Provider
- flutter_reactive_ble
- lottie · flutter_svg
- permission_handler · app_links
Auth · Backend
- Firebase Auth (Google · Apple)
- Cloud Firestore · Storage
- Cloud Functions v2 (Node 20, asia-northeast3)
- Firebase Hosting (admin dashboard)
Quality
- flutter_lints + custom analysis_options
- Domain-logic unit tests (cycle logging · HR zones · BLE UUID)
- iOS · Android build automation
Outcomes
Outcomes
- ✓iOS and Android release builds delivered in about seven months, implementing the client's designs faithfully with follow-up extension points (payments, clinic integration) marked in code
- ✓Auditable admin-access structure appropriate for a service handling fertility medical data
- ✓Korean dartdoc on most classes and constants explaining 'why this value', keeping the codebase maintainable
Next Project
HAIM HAIM