Challenge
A gym is three businesses wearing one uniform. Trainees want workouts and nutrition that adapt to them. Managers want attendance, subscriptions, and revenue they can actually see. Coaches want to sell packages and keep clients without running their business out of a chat thread.
Most fitness software picks one of those three and treats the others as an afterthought. Building three separate apps solves the product problem and creates a maintenance problem — three release cycles, three auth flows, three places for a bug to hide.
Solution
Our team built one Flutter application with a single entry point that branches by role, so trainee, manager, and coach experiences share navigation, theming, auth, and networking rather than reimplementing them. Behind it sits a .NET 10 ASP.NET Core API structured in Clean Architecture with CQRS, so the read paths that power dashboards stay separate from the write paths that move money.
Pose analysis was the deciding constraint. Streaming video to a server for form checking is expensive, leaks privacy, and fails on a gym's wifi — so that work runs on-device with Google ML Kit, and the backend only ever receives results. SignalR carries live updates where they genuinely need to be live, and Redis absorbs the read pressure that attendance and subscription screens generate.
The commerce side went in deliberately: Paymob for payments first, then marketplace payouts so coaches get paid, then gateway payouts, and finally an offline commission mode for gyms that still take cash at the door.
Results
The platform covers the full loop — a trainee books and trains, a coach sells and delivers, a manager watches attendance and revenue, and money moves correctly in each direction. Eighteen delivered feature cycles took it from the first platform spec through AI pose analysis, payments, payouts, an admin dashboard, and a production-hardening pass covering scale, CI/CD, and observability.
Because the three roles share one codebase, a fix to auth or networking ships to all of them at once instead of three times.






