TruFitness
TruFitness
Offline-first workout tracking.
Implemented · tested (static).
TruFitness lets lifters log every set in seconds, with no account and no signal needed. It remembers past performance, flags personal records, suggests the next load for your goal, calculates plates and times rests. All data stays on the phone and survives restarts and app upgrades.


- 19screensworkout, history, templates, stats, profile and more
- 8feature modules0 of 50 domain files import Flutter
- 149test filesDrift schema 18, migrations tested
Purpose
Lets lifters log every set in seconds, with no account and no signal needed, and keeps all data on the phone.
The problem
Logging has to be fast and work without a connection, and a lifter’s history has to survive restarts and app upgrades.
What it contributes
An offline-first Flutter app with its own domain logic for records, load suggestions and plate maths.
Principal capabilities
Mobile frontend
A Flutter app built with feature-first clean architecture: domain, application, data and presentation layers across six features, with domain entities that import no framework code. Riverpod drives state, and mutable workout entities use identity equality so change detection stays correct.
Data
Persistence is SQLite through Drift at schema version 18, reached through an additive migration chain protected by tests that build old database files and open them through the upgrade path.
Domain logic
Estimated one-rep max, personal records that ratchet within a session, volume and cardio-rate records, a goal-driven load-suggestion generator that degrades gracefully when history is thin, and pure plate-layout maths in integer quarter-pound units to avoid floating-point drift.
Built with
- Flutter
- Dart
// Quarter-pound integer total of plate weight to distribute (both sides).
final totalQ = ((targetWeight - barWeight) * 4).round();
// Target below the bar — nothing to load.
if (totalQ < 0) {
return const PlateLayout(perSide: [], remainder: 0, belowBar: true);
}
// Split per side; an odd quarter-pound can't sit symmetrically on both sides,
// so the per side takes the floor and the odd quarter is owed to the
// remainder.
final perSideQ = totalQ ~/ 2;
final oddQ = totalQ - perSideQ * 2; // 0 or 1Implementation evidence
- 149 test files.
- Delivered as a long series of small, numbered briefs, each landing as a reviewed commit.
Visuals




