A multi-sport fitness tracking app built with Flutter that allows users to log workouts, track progress over time, and unlock achievement badges — all offline-first using local storage.
The app currently supports:
- 🏊 Swimming
- 🏃 Running
- 🚴 Biking
Each sport has its own tailored input flow, statistics, and badge evaluation while sharing a common data and UI structure.
- Log workouts with sport-specific inputs:
- Swimming: distance per lap + number of laps
- Running: distance + start & end time
- Biking: distance + start & end time
- Date picker (defaults to today)
- Swipe-to-delete entries
Each sport shows:
- Total distance
- Calories burned
- Time spent (for running & biking)
- Sport-specific KPIs
- Tab-based layout:
- Entries list
- Graphs & statistics
- Charts powered by
fl_chart
- Unified badge system across all sports
- Badges unlock dynamically based on activity history
- Badges are revoked if requirements are no longer met
- Horizontally scrollable badge row
- Badge row only appears when at least one badge is unlocked
- All data stored locally using
shared_preferences - No backend required
- Data persists across app restarts
lib/
├── components/
│ ├── swimming.dart
│ ├── running.dart
│ ├── biking.dart
│ └── shared_widgets/
├── models/
│ ├── swim_entry.dart
│ ├── run_entry.dart
│ ├── biking_entry.dart
│ ├── badge.dart
│ ├── badge_type.dart
│ └── models.dart
├── utils/
│ └── chart_helpers.dart
├── main.dart
- Lap-based tracking (25m / 50m)
- Automatic distance & calorie calculation
- Distance & calorie trend charts
- Badge unlocks based on distance and consistency
- Distance input
- Start & end time selection
- Automatic duration calculation
- Time-based statistics and badges
- Distance input
- Start & end time selection
- Total ride duration tracking
- Calories, distance & time overview
Calories are calculated per sport using configurable constants:
calories = distance (meters) × kcalPerMeter
- Calculated using start and end time
- Handles overnight activities automatically
- Badges are defined centrally
- Each badge:
- Has a
BadgeType - Implements an
isUnlocked(List<Entry>)rule
- Has a
- Badge state is recalculated whenever entries are added or removed
BadgeDefinition( type: BadgeType.firstWorkout, title: 'First Workout', isUnlocked: (entries) => entries.isNotEmpty, )
flutter: sdk: flutter shared_preferences: ^2.x.x fl_chart: ^0.66.x
flutter: assets: - assets/images/swimming.png - assets/images/running.png - assets/images/bike_riding.png
- Weekly & monthly summaries
- Advanced analytics (pace, speed, averages)
- Sport-specific badge categories
- Cloud sync & authentication
- Data export (CSV / JSON)
- Dark mode support
- Flutter
- Material Design
- Stateful Widgets
- Local persistence
- Modular component architecture
This project is intended for personal and educational use.
You are free to modify, extend, and adapt it to your needs.