Complete technical documentation for step counting in the Meltdown app.
┌─────────────────────────────────────────────────────────────────────────────┐
│ MELTDOWN STEP COUNTER │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────┐ │
│ │ Hardware Sensor │ │
│ │ TYPE_STEP_ │ │
│ │ DETECTOR │ │
│ └────────┬────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ AccurateStepCounter (Package) │ │
│ │ • Threshold: 0.6 (API 33+) or 0.9 (API ≤32) │ │
│ │ • Filter: Low-pass (alpha=0.6) │ │
│ │ • Min time between steps: 300ms │ │
│ └────────────────────────────────┬────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ ReadFootStepsCubit (Orchestrator) │ │
│ │ │ │
│ │ ┌──────────────────┐ ┌─────────────────┐ ┌───────────────────┐ │ │
│ │ │ 8-Second Warmup │ │ Session Delta │ │ 2s Inactivity │ │ │
│ │ │ Filter │──│ Calculation │──│ Detection │ │ │
│ │ └──────────────────┘ └─────────────────┘ └───────────────────┘ │ │
│ └────────────────────────────────┬────────────────────────────────────┘ │
│ │ │
│ ┌───────────────────────┼────────────────────┐ │
│ ▼ ▼ ▼ │
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │
│ │ Health Connect │ │ StepTracking │ │ Backend API │ │
│ │ (Write Steps) │ │ Service (UI) │ │ (Every 20 │ │
│ │ │ │ ValueNotifier │ │ writes) │ │
│ └─────────────────┘ └─────────────────┘ └─────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────────────────────────┐
│ FOREGROUND FLOW │
├──────────────────────────────────────────────────────────────────────────┤
│ │
│ 🚀 App Launch │
│ │ │
│ ▼ │
│ ┌────────────────────┐ │
│ │ Platform = Android?│ │
│ └─────────┬──────────┘ │
│ Yes │ No ──────► [Skip step counting] │
│ ▼ │
│ ┌────────────────────────────┐ │
│ │ Check ACTIVITY_RECOGNITION │ │
│ │ Permission │ │
│ └─────────┬──────────────────┘ │
│ │ │
│ ┌────────┴────────┐ │
│ │ │ │
│ ▼ Denied ▼ Granted │
│ [Show UI] ┌─────────────────────────────┐ │
│ │ Sync missed steps from │ │
│ │ terminated state │ │
│ └──────────────┬──────────────┘ │
│ ▼ │
│ ┌─────────────────────────────┐ │
│ │ Start AccurateStepCounter │ │
│ │ stepEventStream.listen() │ │
│ └──────────────┬──────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────┐ │
│ │ 🚶 User Walks... │◄──────────┐ │
│ └──────────────┬──────────────┘ │ │
│ ▼ │ │
│ ┌─────────────────────────────┐ │ │
│ │ Step Event Received │ │ │
│ └──────────────┬──────────────┘ │ │
│ ▼ │ │
│ ┌─────────────────────────────┐ │ │
│ │ Warmup Phase (< 8 seconds)? │ │ │
│ └──────────────┬──────────────┘ │ │
│ │ │ │ │
│ Yes ▼ ▼ No │ │
│ [Ignore step] ┌─────────────────────┐ │ │
│ [Reset timer] │ Write to Health │ │ │
│ │ │ Connect │ │ │
│ │ └─────────┬───────────┘ │ │
│ │ ▼ │ │
│ │ ┌─────────────────────┐ │ │
│ │ │ Counter >= 20? │ │ │
│ │ └─────────┬───────────┘ │ │
│ │ │ │ │ │
│ │ No │ │ Yes │ │
│ │ │ ┌───────▼───────┐ │ │
│ │ │ │ POST to API │ │ │
│ │ │ │ Reset counter │ │ │
│ │ │ └───────┬───────┘ │ │
│ └───────────────┴────────────┴─────────────┘ │
│ │
└──────────────────────────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────────────────────────┐
│ BACKGROUND FLOW │
├──────────────────────────────────────────────────────────────────────────┤
│ │
│ 📱 App Goes to Background │
│ │ │
│ ▼ │
│ ┌────────────────────────┐ │
│ │ Android API Version? │ │
│ └──────────┬─────────────┘ │
│ │ │
│ ┌────────┴────────┐ │
│ │ │ │
│ ▼ API ≤ 32 ▼ API ≥ 33 │
│ │
│ ┌────────────────────────────────┐ ┌────────────────────────────────┐ │
│ │ FOREGROUND SERVICE MODE │ │ HEALTH CONNECT MODE │ │
│ │ (Android 10-12) │ │ (Android 13+) │ │
│ │ │ │ │ │
│ │ ┌──────────────────────────┐ │ │ ┌──────────────────────────┐ │ │
│ │ │ FlutterForegroundTask │ │ │ │ App suspended by OS │ │ │
│ │ │ running in background │ │ │ │ No active counting │ │ │
│ │ └────────────┬─────────────┘ │ │ └────────────┬─────────────┘ │ │
│ │ ▼ │ │ ▼ │ │
│ │ ┌──────────────────────────┐ │ │ ┌──────────────────────────┐ │ │
│ │ │ 🔔 Notification shown: │ │ │ │ Health Connect may │ │ │
│ │ │ "Tracking your steps" │ │ │ │ passively record steps │ │ │
│ │ └────────────┬─────────────┘ │ │ └────────────┬─────────────┘ │ │
│ │ ▼ │ │ ▼ │ │
│ │ ┌──────────────────────────┐ │ │ ┌──────────────────────────┐ │ │
│ │ │ AccurateStepCounter │ │ │ │ Steps synced when app │ │ │
│ │ │ continues detecting │ │ │ │ reopens │ │ │
│ │ │ Steps written in │ │ │ └──────────────────────────┘ │ │
│ │ │ real-time │ │ │ │ │
│ │ └──────────────────────────┘ │ │ │ │
│ │ │ │ │ │
│ └────────────────────────────────┘ └────────────────────────────────┘ │
│ │
└──────────────────────────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────────────────────────┐
│ TERMINATED STATE RECOVERY │
├──────────────────────────────────────────────────────────────────────────┤
│ │
│ 💀 App Killed by OS │
│ │ │
│ │ Native SharedPreferences retains: │
│ │ • Last step count │
│ │ • Timestamp of last save │
│ │ │
│ ▼ │
│ ┌────────────────────────────┐ │
│ │ SYNC TRIGGERS │ │
│ └────────────────────────────┘ │
│ │ │
│ ├──────────────────────────────────────────────┐ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌──────────────┐ ┌──────────────┐ ┌────────────────────┐ │
│ │ WorkManager │ │ WorkManager │ │ FCM Silent Push │ │
│ │ 00:05 AM │ │ 12:05 PM │ │ (Server-triggered) │ │
│ │ "midnight- │ │ "noon-step- │ │ │ │
│ │ step-sync" │ │ sync" │ │ step_sync: true │ │
│ └──────┬───────┘ └──────┬───────┘ └─────────┬──────────┘ │
│ │ │ │ │
│ └──────────────────┼─────────────────────┘ │
│ ▼ │
│ ┌────────────────────────────────────────────────────────────────┐ │
│ │ TerminatedStateStepCounterService.syncStepsInBackground() │ │
│ └────────────────────────────────┬───────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌────────────────────────────────────────────────────────────────┐ │
│ │ 1. MethodChannel: getStepCount() │ │
│ │ Get current OS sensor count │ │
│ └────────────────────────────────┬───────────────────────────────┘ │
│ ▼ │
│ ┌────────────────────────────────────────────────────────────────┐ │
│ │ 2. MethodChannel: syncStepsFromTerminated() │ │
│ │ Returns: { missedSteps, startTime, endTime } │ │
│ └────────────────────────────────┬───────────────────────────────┘ │
│ │ │
│ ┌──────────────┴──────────────┐ │
│ │ │ │
│ null │ │ data │
│ ▼ ▼ │
│ ┌─────────────────────────┐ ┌────────────────────────────────────┐ │
│ │ No new steps to sync │ │ 3. Write to Health Connect │ │
│ │ Update baseline only │ │ health.writeHealthData(...) │ │
│ └─────────────────────────┘ └──────────────────┬─────────────────┘ │
│ │ │
│ ┌──────────────┴──────────────┐ │
│ │ │ │
│ success failure │
│ ▼ ▼ │
│ ┌─────────────────────────┐ ┌─────────────────────┐ │
│ │ ✅ Steps synced! │ │ ❌ WorkManager │ │
│ │ Baseline updated │ │ will retry │ │
│ └─────────────────────────┘ └─────────────────────┘ │
│ │
└──────────────────────────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────────────────────────┐
│ APP RESUME FLOW │
├──────────────────────────────────────────────────────────────────────────┤
│ │
│ 📱 App Reopened by User │
│ │ │
│ ▼ │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ ensureStepCountingIsActive() │ │
│ └─────────────────────────────────┬──────────────────────────┘ │
│ ▼ │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ Check activityRecognition permission │ │
│ └─────────────────────────────────┬──────────────────────────┘ │
│ │ │
│ ┌───────────────┴───────────────┐ │
│ │ │ │
│ Not Granted Granted │
│ ▼ ▼ │
│ ┌─────────────────────┐ ┌───────────────────────────────────────┐ │
│ │ Request permission │ │ _syncMissedStepsFromTerminatedState() │ │
│ │ or show denied UI │ └───────────────────────┬───────────────┘ │
│ └─────────────────────┘ ▼ │
│ ┌───────────────────────────────────────┐ │
│ │ MethodChannel: syncStepsFromTerminated│ │
│ └───────────────────────┬───────────────┘ │
│ │ │
│ ┌──────────────────┴──────────────┐ │
│ │ │ │
│ null │ │ │
│ ▼ ▼ │
│ ┌─────────────────────┐ ┌─────────────────────────┐ │
│ │ No missed steps │ │ Write missed steps │ │
│ │ Continue normally │ │ to Health Connect │ │
│ └──────────┬──────────┘ └────────────┬────────────┘ │
│ │ │ │
│ └─────────────┬─────────────┘ │
│ ▼ │
│ ┌───────────────────────────────────────┐ │
│ │ Already counting? (_isStepCountingActive)│
│ └───────────────────────┬───────────────┘ │
│ │ │ │
│ Yes │ │ No │
│ ▼ ▼ │
│ ┌──────────────────┐ ┌───────────────────────┐│
│ │ Skip restart │ │ startContinuousStep ││
│ └────────┬─────────┘ │ Counting() ││
│ │ └───────────┬───────────┘│
│ └────────────┬───────────┘ │
│ ▼ │
│ ┌───────────────────────────────────────┐ │
│ │ 🚶 Ready to track steps! │ │
│ └───────────────────────────────────────┘ │
│ │
└──────────────────────────────────────────────────────────────────────────┘
Prevents false step counts from:
- Phone pickup movements
- Accidental sensor triggers
- Brief non-walking movements
┌──────────────────────────────────────────────────────────────────────────┐
│ 8-SECOND WARMUP LOGIC │
├──────────────────────────────────────────────────────────────────────────┤
│ │
│ Step Event Received │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────┐ │
│ │ Is this a new walking session? │ │
│ │ (_walkingStartTime == null) │ │
│ └───────────────────┬──────────────────────┘ │
│ │ │ │
│ Yes │ │ No │
│ ▼ ▼ │
│ ┌─────────────────────┐ ┌─────────────────────────────┐ │
│ │ START NEW SESSION │ │ Continue existing session │ │
│ │ │ └──────────────┬──────────────┘ │
│ │ • _walkingStartTime │ │ │
│ │ = now │ │ │
│ │ • _sessionStartStep │ │ │
│ │ = sensor count │ │ │
│ │ • _hasWalkingActive │ │ │
│ │ = false │ │ │
│ └──────────┬──────────┘ │ │
│ │ │ │
│ └───────────┬────────────────┘ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────────────┐ │
│ │ Reset 2-second inactivity timer │ │
│ │ (If no step for 2s → session ends, variables reset) │ │
│ └──────────────────────────────────────────┬───────────────────────┘ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────────────┐ │
│ │ Elapsed time since _walkingStartTime? │ │
│ └──────────────────────────────────────────┬───────────────────────┘ │
│ │ │ │
│ < 8 seconds >= 8 seconds │
│ │ │ │
│ ▼ ▼ │
│ ┌─────────────────────────┐ ┌───────────────────────────────┐ │
│ │ WARMUP PHASE │ │ Has warmup just completed? │ │
│ │ │ │ (_hasWalkingActivated == false)│ │
│ │ • Observe steps │ └───────────────┬───────────────┘ │
│ │ • DO NOT write to HC │ │ │ │
│ │ • Log: "⏱ Warmup..." │ Yes │ │ No │
│ │ │ ▼ ▼ │
│ │ [RETURN - ignore step] │ ┌────────────┐ ┌────────────────┐│
│ └─────────────────────────┘ │ FIRST WRITE│ │ DELTA WRITE ││
│ │ │ │ ││
│ │ Write ALL │ │ Write only ││
│ │ steps from │ │ (current - ││
│ │ session │ │ lastWritten) ││
│ │ start │ │ steps ││
│ │ │ │ ││
│ │ Set _has │ │ ││
│ │ Walking │ │ ││
│ │ Activated │ │ ││
│ │ = true │ │ ││
│ └────────────┘ └────────────────┘│
│ │
└──────────────────────────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────────────────────────┐
│ SESSION STATE VARIABLES │
├──────────────────────────────────────────────────────────────────────────┤
│ │
│ DateTime? _walkingStartTime │ When walking session started │
│ ────────────────────────────────────────────────────────────────────── │
│ int? _sessionStartStepCount │ Sensor count at session start │
│ ────────────────────────────────────────────────────────────────────── │
│ int? _lastWrittenSensorCount │ Sensor count at last HC write │
│ ────────────────────────────────────────────────────────────────────── │
│ bool _hasWalkingActivated │ True after 8s warmup completed │
│ ────────────────────────────────────────────────────────────────────── │
│ Timer? _walkingInactivityTimer │ Fires after 2s no steps → reset │
│ │
└──────────────────────────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────────────────────────┐
│ ANDROID VERSION COMPARISON │
├──────────────────────────────────────────────────────────────────────────┤
│ │
│ Android ≤12 (API ≤32) Android 13+ (API ≥33) │
│ ───────────────────────────────────────────────────────────────────── │
│ │
│ Background Foreground Service Health Connect │
│ Method FlutterForegroundTask Passive Tracking │
│ ───────────────────────────────────────────────────────────────────── │
│ │
│ Notification Required Not needed │
│ "Tracking your steps" │
│ ───────────────────────────────────────────────────────────────────── │
│ │
│ Step Threshold 0.9 (less sensitive) 0.6 (more sensitive) │
│ ───────────────────────────────────────────────────────────────────── │
│ │
│ Battery Request exemption Not needed │
│ Optimization │
│ ───────────────────────────────────────────────────────────────────── │
│ │
│ Exact Alarm Required (API 31-32) Not needed │
│ Permission │
│ │
└──────────────────────────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────────────────────────┐
│ SYNC PIPELINE │
├──────────────────────────────────────────────────────────────────────────┤
│ │
│ Write #1 ──► Health Connect ✓ │
│ Write #2 ──► Health Connect ✓ │
│ Write #3 ──► Health Connect ✓ │
│ ... │
│ Write #19 ──► Health Connect ✓ │
│ Write #20 ──► Health Connect ✓ ──► API Sync! 📤 │
│ │ │
│ ▼ │
│ ┌──────────────────────────────┐ │
│ │ POST /leaderboard/steps │ │
│ │ { │ │
│ │ steps: 1234, │ │
│ │ deviceId: "...", │ │
│ │ yesterdaySteps: 5678 │ │
│ │ } │ │
│ └──────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────┐ │
│ │ Reset counter to 0 │ │
│ │ timesWroteInHeathConnect=0 │ │
│ └──────────────────────────────┘ │
│ │
│ Write #21 ──► Health Connect ✓ (cycle repeats...) │
│ │
└──────────────────────────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────────────────────────┐
│ ERROR HANDLING STRATEGY │
├──────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ Health Connect Operation (read/write/status) │ │
│ └──────────────────────────────────┬──────────────────────────────────┘ │
│ │ │
│ ┌────────────────────────┼────────────────────────┐ │
│ │ │ │ │
│ Success PlatformException Other Error │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌──────────────────┐ ┌────────────────────────┐ ┌───────────────────┐ │
│ │ ✅ Emit success │ │ 🛡️ Catch gracefully │ │ ❌ Generic error │ │
│ │ Update UI │ │ │ │ handler │ │
│ └──────────────────┘ │ Log: "Health Connect │ │ │ │
│ │ binding failed" │ │ Emit: failed │ │
│ │ │ │ state │ │
│ │ Emit: sdkNotAvailable │ └───────────────────┘ │
│ │ state │ │
│ │ │ │
│ │ UI shows: "Please │ │
│ │ install Health Connect"│ │
│ └────────────────────────┘ │
│ │
│ ───────────────────────────────────────────────────────────────────── │
│ │
│ ERROR │ HANDLING │
│ ────────────────────────────────────────────────────────────────────── │
│ RemoteException: Binding failed │ sdkNotAvailable state + install msg │
│ Permission denied │ permissionDenied state │
│ SDK not available │ sdkNotAvailable state │
│ Native channel error │ Log & continue │
│ WorkManager failure │ Auto-retry with backoff │
│ │
└──────────────────────────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────────────────────────┐
│ ReadFootStepsState Transitions │
├──────────────────────────────────────────────────────────────────────────┤
│ │
│ [initial] │
│ │ │
│ │ checkHealthConnectStatus() │
│ ▼ │
│ [loading] │
│ │ │
│ ├──────────────────────┬────────────────────┐ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ [sdkNotAvailable] [sdkAvailable] [failed] │
│ │ │ │ │
│ │ Show install │ checkPermission() │ Retry? │
│ │ prompt │ │ │ │
│ ▼ │ │ ▼ │
│ [END] ├──────────┐ └► [loading] │
│ │ │ │
│ ▼ ▼ │
│ [permissionDenied] [permissionGranted] │
│ │ │ │
│ │ Show │ readFootSteps() │
│ │ settings │ │
│ │ prompt ▼ │
│ ▼ [success] │
│ [END] │ │
│ │ steps: int │
│ │ yesterdaySteps: int? │
│ ▼ │
│ [Displaying in UI] ◄──────┐ │
│ │ │ │
│ │ Real-time │ │
│ │ updates │ │
│ └───────────────┘ │
│ │
└──────────────────────────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────────────────────────┐
│ REQUIRED PERMISSIONS │
├──────────────────────────────────────────────────────────────────────────┤
│ │
│ PERMISSION │ ANDROID VERSION │ PURPOSE │
│ ────────────────────────────────────────────────────────────────────── │
│ ACTIVITY_RECOGNITION │ All │ Access step sensor │
│ ────────────────────────────────────────────────────────────────────── │
│ FOREGROUND_SERVICE │ ≤12 only │ Keep app alive │
│ ────────────────────────────────────────────────────────────────────── │
│ FOREGROUND_SERVICE_HEALTH │ ≤12 only │ Health service type │
│ ────────────────────────────────────────────────────────────────────── │
│ POST_NOTIFICATIONS │ 13+ │ Show notification (≤12) │
│ ────────────────────────────────────────────────────────────────────── │
│ SCHEDULE_EXACT_ALARM │ 12 only │ Reliable WorkManager │
│ ────────────────────────────────────────────────────────────────────── │
│ Health Connect R/W │ All │ Read/Write step data │
│ │
└──────────────────────────────────────────────────────────────────────────┘
| File | Purpose |
|---|---|
| main.dart | App entry, WorkManager init, FCM handler |
| read_foot_steps_cubit.dart | Main step counting orchestration |
| foreground_step_counter_service.dart | Foreground service for Android ≤12 |
| terminated_state_step_counter_service.dart | Background sync & WorkManager tasks |
| step_tracking_service.dart | UI state management with ValueNotifier |