accurate_step_counter 2.0.0 copy "accurate_step_counter: ^2.0.0" to clipboard
accurate_step_counter: ^2.0.0 copied to clipboard

PlatformAndroid

Production-grade step counter for Flutter Android. Uses native TYPE_STEP_COUNTER sensor via foreground service for accurate tracking in foreground, background, and terminated states. Includes SQLite l [...]

Accurate Step Counter #

pub package License: MIT Production Ready

A production-grade, accurate step counter for Flutter on Android. Uses the native TYPE_STEP_COUNTER sensor via a foreground service for reliable tracking across foreground, background, and terminated app states.

โœจ Features #

  • ๐ŸŽฏ Hardware Accurate โ€” Uses Android's TYPE_STEP_COUNTER sensor (cumulative, boot-relative)
  • ๐Ÿ’พ Persistent Storage โ€” SQLite database with reactive streams and background isolate support
  • ๐Ÿ“ฑ All App States โ€” Foreground, background, AND terminated state recovery
  • ๐Ÿ”€ Smart Merge โ€” SmartMergeHelper.mergeStepCounts() for combining sensor + Health Connect + server sources
  • ๐Ÿ”‹ Battery Efficient โ€” Event-driven architecture with notification throttling
  • โฑ๏ธ Warmup Validation โ€” Filters out shakes and false positives with sliding window validation
  • ๐Ÿงต Low-End Device Support โ€” Optional background isolate for smooth UI on budget devices
  • ๐ŸŒ External Import โ€” Import steps from Google Fit, Apple Health, etc.
  • ๐Ÿ•› Midnight Day Reset โ€” Automatic day boundary handling with alarm-based midnight reset

๐Ÿ“ฑ Platform Support #

Platform Status Note
Android โœ… Full support (API 24+) Native TYPE_STEP_COUNTER + foreground service
iOS โŒ Not supported

๐Ÿš€ Quick Start #

1. Install #

dependencies:
  accurate_step_counter: ^2.0.0

2. Add Permissions #

In android/app/src/main/AndroidManifest.xml:

<uses-permission android:name="android.permission.ACTIVITY_RECOGNITION"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_HEALTH"/>
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>

3. Use It #

import 'package:accurate_step_counter/accurate_step_counter.dart';

final stepCounter = AccurateStepCounter();

// Initialize database + start detection + start logging
await stepCounter.initializeLogging(useBackgroundIsolate: true);
await stepCounter.start(config: StepDetectorConfig.walking());
await stepCounter.startLogging(config: StepRecordConfig.aggregated());

// Watch today's steps (emits immediately with stored value)
stepCounter.watchAggregatedStepCounter().listen((steps) {
  print('Steps today: $steps');
});

๐Ÿ“– Complete Example #

import 'package:flutter/material.dart';
import 'package:accurate_step_counter/accurate_step_counter.dart';
import 'package:permission_handler/permission_handler.dart';

class StepCounterPage extends StatefulWidget {
  @override
  State<StepCounterPage> createState() => _StepCounterPageState();
}

class _StepCounterPageState extends State<StepCounterPage>
    with WidgetsBindingObserver {
  final _stepCounter = AccurateStepCounter();
  int _steps = 0;

  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance.addObserver(this);
    _init();
  }

  Future<void> _init() async {
    await Permission.activityRecognition.request();

    await _stepCounter.initializeLogging(useBackgroundIsolate: true);
    await _stepCounter.start(config: StepDetectorConfig.walking());
    await _stepCounter.startLogging(config: StepRecordConfig.aggregated());

    _stepCounter.watchAggregatedStepCounter().listen((steps) {
      setState(() => _steps = steps);
    });

    _stepCounter.onTerminatedStepsDetected = (steps, from, to) {
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text('Synced $steps missed steps!')),
      );
    };
  }

  @override
  void didChangeAppLifecycleState(AppLifecycleState state) {
    _stepCounter.setAppState(state);
  }

  @override
  void dispose() {
    WidgetsBinding.instance.removeObserver(this);
    _stepCounter.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Text('$_steps steps', style: TextStyle(fontSize: 48)),
      ),
    );
  }
}

๐Ÿ”€ Smart Merge (NEW in v2.0.0) #

Combine multiple step sources for maximum reliability โ€” the pattern used by production apps:

import 'package:accurate_step_counter/accurate_step_counter.dart';

final merged = SmartMergeHelper.mergeStepCounts(
  sensorSteps: await stepCounter.currentStepCount,
  healthConnectSteps: hcSteps ?? 0,
  serverSteps: serverRecoveredSteps,
  currentDisplayed: displayedCount,
);
// Always returns the highest reliable value โ€” never goes backwards

๐Ÿ—๏ธ Architecture #

Android TYPE_STEP_COUNTER sensor
    โ†“
StepCounterService.kt (Foreground service with notification)
    โ†“ LocalBroadcastManager
AccurateStepCounterPlugin.kt (MethodChannel + EventChannel bridge)
    โ†“ EventChannel stream
step_counter_platform.dart (Dart platform interface)
    โ†“
AccurateStepCounterImpl (Detection + SQLite logging + aggregation)
    โ†“
Your App (watchAggregatedStepCounter / SmartMergeHelper)
App State Behavior
๐ŸŸข Foreground Real-time step events via EventChannel
๐ŸŸก Background Foreground service keeps counting
๐Ÿ”ด Terminated TYPE_STEP_COUNTER sync on restart + midnight alarm reset

๐Ÿ”ง API Reference #

Core Methods #

Method Description
start() Start step detection
stop() Stop step detection
isNativeStepServiceRunning() Check if native service is alive
isUsingNativeStepService Whether native service mode is active
currentStepCount Current steps since start()

Aggregated Mode #

Method Description
initializeLogging() Initialize SQLite database
startLogging() Start recording steps to database
watchAggregatedStepCounter() Real-time stream (stored + live)
aggregatedStepCount Sync getter for current total
getTodaySteps() Today's total from database
getYesterdaySteps() Yesterday's total from database
getStepsInRange() Steps for custom date range
writeStepsToAggregated() Import external steps

Smart Merge #

Method Description
SmartMergeHelper.mergeStepCounts() Merge sensor + HC + server (max of all)
SmartMergeHelper.mergeSensorAndHealth() Simplified merge (sensor + HC only)

Debug #

Method Description
StepLogsViewer Widget for viewing step logs with filters
getStepLogs() Get all step log entries
getStepStats() Get statistics (totals by source, averages)

โš™๏ธ Configuration #

// Presets
await stepCounter.start(config: StepDetectorConfig.walking());
await stepCounter.start(config: StepDetectorConfig.running());
await stepCounter.start(config: StepDetectorConfig.sensitive());
await stepCounter.start(config: StepDetectorConfig.conservative());

// Custom
await stepCounter.start(config: StepDetectorConfig(
  threshold: 1.2,
  filterAlpha: 0.85,
  minTimeBetweenStepsMs: 250,
  enableOsLevelSync: true,
  useForegroundServiceOnOldDevices: true,
  foregroundServiceMaxApiLevel: 29,
));

// Logging presets
await stepCounter.startLogging(config: StepRecordConfig.aggregated());
await stepCounter.startLogging(config: StepRecordConfig.lowEndDevice());

๐Ÿ”’ Reliability Features #

  • Idempotency keys โ€” Deterministic keys prevent duplicate records
  • Single-writer queue โ€” Serialized database writes prevent race conditions
  • Mutex locks โ€” Concurrent writeStepsToAggregated() calls are serialized
  • Warmup validation โ€” Sliding window filters shakes and non-walking motion
  • Terminated sync โ€” Deterministic gap identity prevents double-counting
  • Midnight reset โ€” AlarmManager-based day boundary handling
  • Cold start recovery โ€” Database auto-reopens after Android kills the app

๐Ÿ“ฑ Low-End Device Support #

// Background isolate moves all DB work off the main thread
await stepCounter.initializeLogging(useBackgroundIsolate: true);
await stepCounter.startLogging(config: StepRecordConfig.lowEndDevice());

๐Ÿ“„ License #

MIT License โ€” see LICENSE

3
likes
150
points
698
downloads

Documentation

API reference

Publisher

verified publisherrahulsha.com.np

Weekly Downloads

Production-grade step counter for Flutter Android. Uses native TYPE_STEP_COUNTER sensor via foreground service for accurate tracking in foreground, background, and terminated states. Includes SQLite logging, smart merge utilities, and warmup validation.

Repository (GitHub)
View/report issues

License

MIT (license)

Dependencies

flutter, path, sensors_plus, sqflite

More

Packages that depend on accurate_step_counter

Packages that implement accurate_step_counter