Inspiration
I spend most of my day at a desk, and like most office workers I have the same broken loop: an ache shows up in my back or shoulders, I tell myself I'll "do some stretches later," and later never comes. The apps that do exist are built for 30-minute workouts in athletic clothes — not for someone who has 90 seconds between Slack messages and a sore neck.
PhysTech felt like the right excuse to build the app I actually want on my own phone: a one-handed, voice-led, two-minute physiotherapy companion that listens to how you feel today (pain, sleep, energy, mood) and rearranges itself accordingly — instead of pushing the same generic plan at everyone.
What it does
MoveMate is a physiotherapy-grade micro-coach that runs entirely offline on your phone.
- Tracks the signals that matter — per-area pain (0–10) on an interactive body silhouette, sleep hours and quality, energy, mood, hydration, posture, and full session history.
- Builds an adaptive 7-day plan that responds to those signals: hot lower back at 7/10 promotes a relief day, five hours of sleep swaps cardio for breath, neglected shoulders surface a shoulder-focused flow.
- Coaches by voice — TTS leads every exercise step with one of three personalities (Calm / Upbeat / Strict), each with its own rate and pitch profile. Tap once, follow the voice, get back to your day.
- Celebrates without nagging — daily-goal confetti, streak milestones with freeze protection, 18 achievements with per-badge progress bars, "Moment of Pride" hero card, and a "What's Working" panel that surfaces the last week's positive signals.
- Closes the loop on intensity — any cardio session or anything ≥ 5 minutes triggers a 4-cycle box-breathing wind-down before the mood prompt.
- Shares the way physiotherapists actually want it — one-tap 1080×1920 PNG cards: a 30-day pain report (per-area sparklines, biggest improvement, days logged), a weekly recap, and a daily mantra share card.
- Native Android touches — long-press launcher shortcuts (Pain · Stretch · Breathe), TZ-aware reminders with quiet hours that wrap past midnight, branded gradient splash.
How we built it
Flutter (Dart 3.11+) + Material 3 for the UI, with custom transitions, custom painters (sparklines, hexagonal body-coverage radar, GitHub-style consistency heatmap, breathing visualizations, progress rings), and a 4-tab shell (Home / Tools / Progress / Settings).
- shared_preferences as the only persistence layer, wrapped in a single Storage facade — no provider, bloc, streams, or backend. Every screen reads through it; writes trigger a setState in the shell. The data set is small (hundreds of session records) and the productivity gain of "just a class" is huge.
- Pure-function algorithm modules that take a Storage and return a result: Recommender, SmartCoach, AdaptivePlan, MomentOfPride, WhatsWorking, WellnessScore, PersonalRecords, BodyCoverage, MovementDNA, EnergyHours. They have zero framework deps, which makes them trivially testable.
- flutter_tts with per-personality rate/pitch profiles plus a tap-to-hear voice preview in Settings.
- flutter_local_notifications + timezone + flutter_timezone for reminders, with a quiet-hours filter and a "fire test now" button.
- OverlayEntry + RepaintBoundary for offscreen rendering of the four 1080×1920 share cards.
- Native Android plumbing in Kotlin — MethodChannel between MainActivity.kt and Dart, intent-filters in AndroidManifest.xml, shortcuts.xml for long-press launcher shortcuts.
- GitHub Actions CI running flutter analyze --no-fatal-infos + flutter test on every push.
Challenges we ran into
- Designing for "the body as input." Pain isn't a number; it's where and how much. Building an interactive front-view body silhouette that tints by per-area pain level (and round-trips into the adaptive plan) took several iterations.
- Adaptive plan transparency. Black-box recommendations feel creepy in a health app, so every plan-day card had to surface why it's there ("Targets back + neck", "You haven't worked shoulders recently"). That meant the planner had to return its reasoning, not just a result.
- Celebrating without becoming guilt-trippy. Streaks are addictive but cruel — miss one day and a 60-day streak turns into shame. Solution: streak freezes earned every 7 days, consumed silently. The rescue banner only appears when freezes are gone and the day still has hours left. It's a backstop, not a nag.
- Offscreen 1080×1920 PNG rendering. Getting four different share cards to render reliably without flicker via OverlayEntry + RepaintBoundary + a post-frame callback was finicky. The pattern is now reused for session, pain report, weekly recap, and daily mantra cards.
- A 1.5-pixel overflow on the onboarding "Anything achy?" tiles that only showed up on the physical phone (not the emulator). Caught it via adb screencap, fixed by tightening the GridView aspect ratio.
- Android 12+ splash override. The system SplashScreen API overrides the windowBackground icon with the launcher icon, so a perfect custom splash takes more than just a drawable/. Worked around it by leaning on the gradient colors rather than the monogram.
Accomplishments that we're proud of
- Shipped a single-binary, fully offline, no-account, no-analytics app with 30+ exercises, 18 achievements, 7 algorithm modules, 4 share-card renderers, 20+ screens, native Android shortcuts, and TTS-guided sessions — solo, for a hackathon
deadline.
- 33 passing unit tests that run in under 6 seconds and cover the actual interesting branches: streak gaps, plan variation under sleep deficit + pain flags, quiet-hours wrap-around past midnight, the demo-seeder healing trend, the Moment of Pride priority order, and the What's-Working observation picker.
- A Home dashboard you can act on in one tap from the top of the screen — animated progress ring, quick-action chips, hot-pain alert with sparkline + dynamic blurb + tailored "Soothe" CTA, mood quick-log with inline trend chart, Moment of Pride hero, What's Working panel.
- The "Soothe" loop: when pain ≥ 4/10 in any area, the Home banner builds a tailored 3-minute relief session via ExerciseLibrary.buildQuickPlanForArea — pain → diagnosis → action in literally one tap.
- Physiotherapist-shareable artifacts — the 30-day pain report PNG was specifically designed to be screenshot-shared with a real physio: per-area sparklines, hottest area, biggest improvement, days logged.
- Tone. Pain comes before streaks. Wins are loud (confetti) but never demanded. The sit-timer warns gently and never shouts.
What we learned
- Pure functions are the cheat code for hackathons. Pulling every algorithm into a module that takes Storage and returns a result meant the test suite stayed under 6 seconds and the UI rewrites in the final 48 hours didn't break a single test.
- A shared_preferences facade beats a "real" state-management library when the data set is small and the screens are short-lived. Zero ceremony, zero rebuilds we don't control, every screen explicit about what it reads.
- Test on a physical phone every round. The 1.5-pixel overflow, the splash icon override, and the wake-screen-before-screencap quirk all only surfaced on hardware.
- Celebration moments need to be designed independently. Bundling daily-goal + streak + achievement into one dialog felt cheap; splitting them into three dedicated confetti dialogs that fire in sequence after a session feels like the app is genuinely paying attention.
- Transparency is a UX feature. "You haven't worked shoulders recently" reads as care; "recommended for you" reads as marketing.
What's next for MoveMate
- On-device posture detection via google_mlkit_pose_detection — point the front camera, get a live posture overlay during desk work.
- Health Connect integration — pull step count and heart rate so the recommender has even more signal without asking for it.
- Localization — Polish + English packs (everything is intl-ready).
- Multi-page physiotherapist PDF export — beyond the single PNG, a real clinical report.
- Adaptive plan editing — drag-to-rearrange the 7-day view.
- iOS + Apple Health support once the Android version stabilizes.
- Long term: a small library of physiotherapist-authored protocols (lower-back, post-cycling, post-running, RSI) that drop into the adaptive planner as constraint sets.
Built With
- css
- dart
- html
- javascript
- kotlin
Log in or sign up for Devpost to join the conversation.